I built a tool that watches you scroll Instagram (or TikTok, or whatever) and tells you what you actually stop to look at. Not what you think you look at—what actually catches your attention. This document explains how it works under the hood.
Fair warning: this is a toy, not science. All the thresholds are rough guesses. The scoring dimensions come from psychology research on social media effects, but I'm not claiming any of this is rigorous. I built it because I was curious, and I thought other people might be too.
I could be wrong about what any of this means. But here's how it works.
The first step is figuring out when you stopped scrolling. This is harder than it sounds because videos autoplay—you can't just look for frames that don't change.
I use phase correlation, which tracks the actual movement between frames. When the scroll stops for more than half a second, that's a pause—you stopped to look at something.
Technically, for each pair of consecutive frames, I compute:
where dy is the vertical shift and r is the correlation peak. If |dy| > 3 pixels, you're scrolling. Otherwise, you're dwelling.
The tool samples at 5 fps (every 6th frame at 30fps video) to keep things fast without missing short pauses.
Every post you pause on gets sent to a vision model (Claude Haiku) and scored on five dimensions. These come from psychology research on what makes social media content psychologically "sticky":
Each post gets a score for all five. Nothing special here—just asking a vision model to rate things.
Each post's overall "intensity" is a weighted power mean of its dimension scores. The idea is that a post that's high on multiple dimensions is more intense than one that's high on just one.
For post i with dimension scores s:
where w_d are the dimension weights (appearance: 0.30, idealization: 0.25, arousal: 0.20, negativity: 0.15, aspiration: 0.10) and p = 2 is the power mean exponent. Higher p means posts that are high on multiple dimensions get even higher scores.
Not all pauses are equal. A 5-second stare means more than a half-second glance. I weight each post by how much attention you gave it:
where:
The weights get normalized so they sum to 1.
Here's where it gets interesting. I don't just take the average of all the posts you looked at. I use a version of the peak-end rule from psychology—the idea that we remember experiences primarily by their most intense moment (peak) and how they ended.
The overall distortion score is:
where:
This means your score isn't just about what you saw on average—it's also about the most intense thing you saw and how your session ended.
This is the part I find most interesting. For each dimension, I compute:
The gap between these tells you something about your engagement patterns. If your "stopped on" appearance score is higher than "served," you're dwelling more on appearance-focused content than average. That's not necessarily bad—it's just data.
Here's where it gets more interesting. Instead of just computing a score, I also compute the posterior probability that your feed is "distorted" given what you stopped on. This uses Bayes' rule.
The idea: we have two hypotheses about your feed. H₁ says your feed is distorted (high engagement-bait content), H₀ says it's normal. We observe your attention patterns and ask: which hypothesis is more likely?
In practice, I compute the log likelihood ratio by modeling dimension scores as Beta distributions:
The distorted hypothesis expects higher scores (Beta with α=3, β=2), while the normal hypothesis expects lower scores (Beta with α=2, β=3). This gives us a Bayes factor—how much more likely the distorted hypothesis is given your data.
I interpret the Bayes factor using the Kass & Raftery (1995) scale: BF > 150 is "very strong" evidence, 20-150 is "strong," 3-20 is "positive," and under 3 is basically inconclusive.
This one comes from psychophysics. Signal detection theory was originally developed for radar operators—can you tell signal from noise?—but it applies here too.
I split your posts into "high arousal" (emotionally intense) and "low arousal" (calm), then compute d-prime:
where μ_high and μ_low are mean dwell times on high vs. low arousal content, and σ_pooled is the pooled standard deviation.
Higher d' means you're more "sensitive" to arousal—you consistently dwell longer on emotionally intense content. A d' near 0 means arousal doesn't affect your attention much.
Now we're getting into information theory. Two measures here:
Attention entropy tells you how spread out your attention is:
where p_i is the normalized attention weight for each post. High entropy means you spread your attention evenly; low entropy means you focused intensely on a few posts.
KL divergence measures how far your attention distribution is from uniform:
Higher KL divergence means your attention was more concentrated—you really locked onto certain posts while barely glancing at others.
I also compute mutual information between dwell time and content intensity—how much does knowing the intensity of a post tell you about how long you'll look at it? This measures the coupling between what's shown and what captures your attention.
Finally, some classic psychophysics. The Weber-Fechner law says that perceived intensity is logarithmic—the difference between 1 and 2 feels bigger than the difference between 9 and 10.
I apply this to the raw dimension scores before computing the final intensity. It makes the system more sensitive to differences at low intensities, which matches how human perception actually works.
For each dimension, I also compute a Beta posterior distribution. This treats each score as a "success" (high) or "failure" (low) and updates a prior belief about the true underlying rate.
This gives us 95% credible intervals—a range where the true dimension score probably lies. It's more honest than just reporting a point estimate.
All the specific values (weights, thresholds, exponents) are tasteful guesses. I tuned them on my own scroll recordings until the output felt reasonable. They're not calibrated on any dataset, and I make no claims about their validity.
If you want to mess with them, all the constants are in config.py. The whole thing is open source.
A few things I want to be explicit about:
This is a curiosity tool. Treat it that way.
Your video is processed on the server and deleted immediately after. I don't store your recordings, I don't look at what you scroll through, and I have no interest in your data. The tool runs, gives you results, and forgets you existed.
If you have thoughts about this—whether you think I'm onto something or totally wrong—I'd actually like to hear it. That's part of why I built this: to see what other people think.