Vibin R.

ALL POSTS

Scroll-linked animation without the jank

Why tying animation to element position made my page feel like it sped up and slowed down, and what I did instead.

28 Aug 2026 · 5 min readFrontendAnimationPerformance

Scroll-driven animation looks simple in a demo and gets strange the moment a real page is involved: sections that seem to scroll at different speeds, elements frozen halfway through their animation on load, and a deck of cards that starts already half-dealt.

The speed illusion

Wheel events are not evenly sized. One notch can be 40 pixels on one device and 120 on another, and trackpads fire bursts of tiny deltas. Put a scroll-linked animation next to a plain section and the page appears to change pace, even though the scroll distance per pixel never changed.

ts

// every wheel event only moves a TARGET
target = clamp(target + e.deltaY);

// each frame glides a fixed fraction of the way there
current += (target - current) * (1 - Math.exp(-dt * SMOOTHING));
window.scrollTo(0, current);

The exponential makes it frame-rate independent, so it feels the same at 60Hz and 144Hz. Everything else — keyboard, scrollbar, find-in-page, touch — is left completely native, because those already behave.

Anchor to the page, not the element

The second problem was subtler. Anchoring progress to each element's position in the viewport means that anything sitting near the fold on load is already partway through its animation before the visitor has scrolled at all. Measuring from absolute page scroll instead guarantees the sequence starts at zero.

The fix that made the biggest visual difference, though, was easing each phase rather than running them linearly. Half linear, half sine keeps the peak rate low, so an element never appears to move faster than the page itself.