Motion technique study
Four ways to move a pixel with JavaScript.
keel ships zero JavaScript by design — its
animations are CSS, and every one of them is gated behind
prefers-reduced-motion. This page is the deliberate
exception: a reference for the handful of effects that need a
script, built the same way you'd build them in a real project —
small, page-local, and contained to one section each.
Every technique below checks
matchMedia("(prefers-reduced-motion: reduce)") before
it starts. If your system asks for reduced motion, each
section falls back to a static, fully readable state instead of
running its animation loop — try it in your OS accessibility
settings and reload.
01 · Pointer tracking
Cursor follower
A dot eases toward the pointer instead of
snapping to it: each frame it moves a fraction of the remaining
distance (linear interpolation, or "lerp"), driven by
requestAnimationFrame. The pointermove
listener is bound to this box alone, not document,
so it never reads mouse movement anywhere else on the page.
Move your pointer inside this box
02 · Scroll progress
Scroll-driven scene
This box scrolls internally
(overflow-y: auto) so the effect stays put instead
of hijacking the page. A scroll listener on the box
itself computes progress and fades/rises a card into place —
pure scroll-position math, no library.
Scroll down inside this box ↓
Frame 04
The card resolves as you scroll
Opacity and position are set from the box's own scroll offset — reload with reduced motion on and this card just stays fully visible, no listener attached.
03 · Layered depth
Parallax on scroll
Three circles move at three different
rates as the page scrolls this section into and out of view —
the classic depth illusion. One shared
requestAnimationFrame loop reads this section's
getBoundingClientRect() each frame and stops
updating once the section leaves the viewport, so it never runs
unnecessarily.
04 · Pointer-relative rotation
Tilt-on-hover card
The card rotates on two axes based on
where your pointer sits inside it — rotateX from
vertical position, rotateY from horizontal —
applied with a plain CSS transform, reset on
pointerleave. No animation loop needed; the
browser's own compositor handles the easing via a CSS
transition.
Hover to tilt
Depth without a library
Twelve lines of JS, one CSS transition, and a perspective on the wrapper.
That's the whole toolbox
Four techniques, four
small functions, one reduced-motion check apiece. Copy the
<script> at the bottom of this file's source —
it's the entire dependency.