Published 27 August 2025 · Updated 14 August 2026 · 8 min read
A cursor-driven frame animation lives or dies on one thing: whether the next frame is already in memory when the pointer moves. Miss that and the effect collapses into a stutter that no amount of visual polish can rescue.
The core loop
- Decode every frame before the interaction is enabled.
- Map pointer X within the container to a frame index.
- Apply the index inside a single animation frame callback, not on every raw event.
- Keep all frames mounted and toggle visibility, so no decode happens mid-swipe.
Why toggling beats swapping
Changing an image source forces the browser to fetch or at least re-decode. Rendering all frames stacked and switching which one is visible avoids that entirely: the work becomes a compositor-level change, which is cheap and predictable.
The trade-off is memory. With a couple of dozen modest frames it is irrelevant; with hundreds of large photographs it is not, and a windowed approach that keeps neighbouring frames warm becomes the better design.
Handling input properly
Pointer events cover mouse, touch and pen in one code path, which removes an entire class of mobile bugs. Throttle to the display refresh rate with a requestAnimationFrame gate so a fast swipe does not queue dozens of redundant updates.
Keyboard support costs almost nothing and makes the toy usable by people who cannot drag: arrow keys step a frame, Home and End jump to either extreme.
Respecting the user
- Honour reduced-motion preferences by offering a static or stepped mode.
- Give the control a role, a label and a live description so assistive technology can report progress.
- Show a loading state until decoding finishes rather than exposing a broken half-ready interaction.
The Eel Slap Archive editorial team
We research early-web culture and document how it was built. Every page cites its sources and is reviewed before publication. Last reviewed 14 August 2026. Spotted an error? Send a correction.