One engine, four behavioursPARALLAX

Scroll — the layers pull apart

01 — Hero layers

The picture moves slower than you do.

The photograph travels at 0.40× the page while the headline runs the other way at −0.35×. The gap between them is the whole effect. Both rates are declared in markup and clamped to the hero, so nothing drifts once the section has left the screen.

0.40×Background rate
−0.35×Headline rate
−0.60×Sub-line rate
1Scroll listener, page-wide

Markup: data-parallax="0.4" on the picture, data-parallax="-0.35" data-parallax-fade="0.85" on the headline. The wrapper carries data-parallax-scene, which is what the rates are measured against.

Depth follows the pointer.

Move the mouse · four layers, one eased position

02 — Pointer depth

A slow, heavy response.

The pointer is normalised to −1…1 and eased toward the cursor a few percent per frame. Each layer multiplies that one eased position by its own depth, so the near layers swing and the far ones barely move. Slow damping is what makes it read as weight rather than as a mouse-follow.

Markup: data-parallax-mouse="0.1""0.8". Add data-parallax-mouse-axis="x" to lock an axis. Coarse pointers are skipped, so phones never pay for it.

Scroll fast — the scene pulls back

03 — Velocity zoom

Speed reads as distance.

The engine measures scroll speed in viewport-heights per frame, frame-rate independent, and drives the scene's scale with it. Fling the page and it pulls back; stop and it eases in again. Panels drift sideways while it is out, which sells the pull-back.

Markup: data-parallax-scene data-parallax-zoom on the stage, data-parallax-zoom-shift on anything that should slide. Two ramps ship: velocity is continuous, fast-mode holds a state until you slow down. Switch between them in the panel.

04 — Card media

Pictures drift inside their frames.

Each picture is taller than the frame that clips it and slides through that spare height as the card crosses the screen. Hovering settles it back down. The travel is one number in the markup; the module writes the CSS variable the frame reads.

Markup: data-parallax-clip="56" on the picture and data-parallax-hover on the card. Hover is a single delegated listener for the whole grid, and it is off on touch.

05 — Cost

One listener, one frame.

Every element on this page — hero layers, depth layers, panels, cards — is driven by the same passive scroll listener and the same rAF batch. Boxes are measured once, on resize, never per frame. The loop stops when everything has settled.

Press H to hide the panel. Set the breakpoint slider above the window width to watch the whole thing fall back to a static layout.