Scroll Velocity Motion Blur — Free Real Velocity-Based CSS Blur Effect

Scroll Velocity Motion Blur · Scroll · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Real velocity measurement
Pixels-per-frame computed from actual scroll deltas.
Two-stage smoothing
Velocity and blur each ease, avoiding jitter.
Decays on stop
An rAF loop clears blur even with no final scroll event.
Live velocity meter
A visible readout of the number driving the effect.
GPU-friendly filter
Only the CSS filter property animates.
Frame-normalized speed
Consistent readings regardless of event timing.
No dependencies
Pure vanilla JS and requestAnimationFrame.
Tunable intensity
MAX_BLUR and smoothing factors are simple constants.

About this UI Snippet

Scroll Velocity Motion Blur — Blur That Tracks Real Scroll Speed

Screenshot of the Scroll Velocity Motion Blur snippet rendered live

This isn't a fixed "blur while scrolling, sharp when still" toggle — the blur amount is a genuine, continuously computed function of how fast the page is actually moving. Scroll slowly and the content stays crisp; flick the page hard and it visibly smears with filter: blur(), then eases back to zero as your scroll speed drops. Built entirely with vanilla JavaScript and requestAnimationFrame, no animation library required.

Measuring real velocity, not just "is scrolling"

Every scroll event records the current window.scrollY, compares it against the value from the previous event, and divides by the elapsed time — normalized to a 16.67ms (60fps) frame so the result reads as "pixels moved per frame" regardless of how often the browser actually fires scroll events. That raw instantaneous velocity is smoothed with a simple exponential-moving-average step (velocity += (instVelocity - velocity) * 0.35) so a single noisy event doesn't spike the blur.

A second smoothing pass for the visual blur

The blur amount applied to the content doesn't jump straight to the velocity-derived target either — a requestAnimationFrame loop eases currentBlur toward targetBlur every frame with its own smoothing factor. Two smoothing stages (velocity, then blur) is what gives the effect a soft ramp-up and a soft settle rather than a jittery on/off flicker, while the underlying number driving it all is still a real speed measurement.

Decay when scrolling stops

Because scroll events stop firing entirely once the page is still, the rAF loop separately checks how long it's been since the last event and decays the velocity toward zero if none has arrived in the last couple of frames. Without this, a scroll that stops abruptly (rather than decelerating smoothly, as happens with certain trackpads or a scrollbar drag) would leave the blur stuck at its last value with nothing to bring it back down.

Live velocity readout

A small fixed meter in the corner displays the raw pixels-per-frame velocity number in real time, which makes the relationship between scroll speed and blur amount directly observable rather than something you have to take on faith — useful for tuning MAX_BLUR and the smoothing constants to taste.

Customizing it

Raise MAX_BLUR for a more dramatic smear, tighten BLUR_SMOOTHING for a snappier response, or apply the blur to individual cards instead of the whole list for a more localized effect. Pair it with scroll skew velocity for a combined blur-and-skew treatment, or contrast it with a scroll reveal grid for a calmer entrance.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet's JS into an AI coding assistant like Claude and ask it to walk through the two-stage smoothing pipeline — why the raw per-event velocity is smoothed once with an exponential moving average before becoming a "target," and why the actual applied CSS blur value is then smoothed a second time toward that target inside the requestAnimationFrame loop, rather than either value being applied directly and instantly. It's also useful for tuning: ask it whether MAX_BLUR of 14px and a BLUR_SMOOTHING of 0.18 feel right for a hero section versus a dense card list, or to add a floor so a very slow, deliberate scroll never triggers even a hint of blur. It can also help extend the idea — applying directional blur (only horizontal or only vertical) based on scroll direction, or scaling blur per-element based on each element's distance from viewport center.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "scroll velocity motion blur" effect in plain HTML, CSS, and vanilla JavaScript (no libraries).

Requirements:
- A scrollable content area (a list of cards or similar) that gets a CSS filter: blur() applied, with the blur amount computed from real, measured scroll velocity — not a fixed value toggled on/off based on whether scrolling is happening.
- On each scroll event, compute an instantaneous velocity as the pixel distance scrolled since the previous scroll event divided by the elapsed time in milliseconds, normalized to a 60fps frame (i.e. treat 16.67ms as one frame) so the result reads as "pixels moved per frame."
- Smooth that raw per-event velocity with an exponential moving average (e.g. velocity += (instantVelocity - velocity) * 0.35) so a single noisy event doesn't spike the result.
- Run a separate requestAnimationFrame loop that: (a) if no scroll event has fired in roughly the last 2 frames, decays the velocity toward zero so the blur clears even if scrolling stops abruptly with no final low-velocity event; (b) computes a target blur amount in pixels proportional to the smoothed velocity, clamped to a maximum (e.g. 14px); (c) eases the actually-applied blur value toward that target with its own smoothing factor; (d) writes the result to the element's filter style only when it changes meaningfully.
- Add a small fixed-position readout on screen showing the live numeric velocity value so the relationship between scroll speed and blur amount is directly observable.
- Confirm behavior by testing: scrolling slowly should produce little to no blur, flicking the page fast should produce a clearly visible blur that scales with how hard you flick, and stopping (even abruptly) should bring the blur back to exactly zero within a few frames — not leave it stuck.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSAn intro, a card list, a meter, and an outro render.
  2. 2
    Scroll slowlyThe meter stays low and the cards remain sharp.
  3. 3
    Flick-scroll fastThe meter spikes and the cards visibly blur.
  4. 4
    Stop suddenlyBlur eases back to zero even without a final scroll event.
  5. 5
    Watch the meterIt shows the live pixels-per-frame value driving the blur.
  6. 6
    Tune the responseAdjust MAX_BLUR and the smoothing constants to taste.

Real-world uses

Common Use Cases

Long-scroll editorial
Add kinetic feel to fast reader scrolling.
Portfolio sites
Pair with scroll skew velocity cards.
Product galleries
Blur thumbnails during fast flicks, sharpen on settle.
Marketing pages
Add a tactile, physical feel to a long scroll.
Image-heavy feeds
Contrast with a calmer scroll reveal grid.
Interactive demos
Show the live velocity meter as a teaching tool.

Got questions?

Frequently Asked Questions

Yes. Each scroll event computes the pixel distance moved since the previous event divided by elapsed time, normalized to a 60fps frame, producing a real "pixels per frame" velocity number. That number (after smoothing) directly sets the blur target in pixels, so a fast flick produces measurably more blur than a slow scroll — verifiable by watching the live meter track both.

Raw scroll events are noisy and irregularly timed, so smoothing the velocity first with an exponential moving average avoids a single jumpy event spiking the number. Then easing the applied blur toward that smoothed velocity target, rather than snapping to it every frame, is what gives the blur a soft ramp instead of a flicker — two lightweight smoothing passes instead of one heavier one.

Scroll events simply stop firing once the page is still, so a separate requestAnimationFrame loop tracks how long it has been since the last scroll event and decays the velocity toward zero if none has arrived within about two frames. Without this check, a scroll that stops instantly (rather than decelerating) would leave the last measured blur amount stuck on screen indefinitely.

The scroll listener is passive and does only cheap arithmetic — no DOM reads that trigger layout. The actual style write (filter) happens once per animation frame in the rAF loop rather than once per scroll event, and filter/blur is GPU-composited, so it stays smooth even during a fast, sustained scroll.

Attach the scroll listener and start the rAF loop in a mount effect, storing velocity and currentBlur in refs rather than plain variables so they persist across renders without triggering re-renders themselves; apply the computed blur directly via a ref's style property. Return a cleanup that removes the scroll listener and cancels the animation frame.