/scrollTest · cinematic scroll lab

Scroll likea film system.

A no-dependency Next.js route reverse-engineering the design-agency playbook: pinned chapters, parallax depth, scroll-scrubbed filmstrips, mask reveals, foreground/background 3D geometry with trailing motion, native view timelines, and a practical library map.

rAF
scroll progressCSS variables as timeline

Why agencies charge for this

It is storyboarding, asset direction, browser math, and QA disguised as vibes.

01Timeline choreography

Every viewport has a beat: reveal, hold, transform, release. The page is edited like a trailer.

02Performance constraint

Transforms, opacity, masks, canvas, and WebGL need a motion budget or the spectacle collapses on mobile.

03Fallback discipline

The expensive part is keeping the story readable with JS disabled, reduced motion, odd screens, and real content.

04Scene-linked 3D depth

Fixed foreground and background geometry changes position, rotation, scale, color, and trail lag as the active scene changes.

Pinned storyboard

One sticky viewport, four moving shots.

This section uses a tall scroll container, a sticky 100vh stage, and JavaScript that writes local progress into CSS variables. The cards move because CSS reads --story-shift.

01

Pin the camera, move the world

The expensive-looking trick is often a sticky section that holds the viewport while every layer scrubs through a storyboarded timeline.

position: sticky + section-local progress
02

Separate depth layers

Foreground dust, midground type, and background glow all move at different rates so a flat DOM page behaves like a camera move.

parallax CSS variables from requestAnimationFrame
03

Reveal with masks, not fades

Clip paths, gradient masks, and drawn SVG paths feel more cinematic than opacity-only entrance animations because they imply physical light.

clip-path, mask-image, stroke-dashoffset
04

Make the page teach the technique

The best agency pages are not random scroll candy. Each movement demonstrates the product story while hiding the engineering seam.

semantic content + choreographed motion budget

Mask reveal + SVG draw

Do not just fade things in. Let the page develop like film.

The diagonal beam below is clipped by --scene-clip. The signal path is drawn by changing stroke-dashoffset. Both are cheap, readable, and art-directable.

Vertical scroll → horizontal reel

A common agency move: scroll down, travel sideways.

01 · Pinned reel

Horizontal travel inside vertical scroll

A tall section supplies scroll distance while the inner stage sticks to the viewport and translates a track sideways.

sticky top: 0; transform: translate3d(var(--reel-x), 0, 0)
02 · Scroll scrub

Progress is the timeline

Instead of playing animation on enter, bind the timeline to local scroll progress so users can scrub frame-by-frame.

progress = clamp(-rect.top / (rect.height - viewport))
03 · Mask wipe

Use light as the transition

Gradient overlays and clip-path reveals make sections feel art-directed instead of componentized.

clip-path: inset(0 calc(100% - var(--scene-clip)) 0 0)
04 · Reduced motion

The hidden professional detail

Flashy does not mean hostile. Premium builds still respect reduced-motion and keep content readable without choreography.

@media (prefers-reduced-motion: reduce) { transform: none; }

Research ledger

The useful toolkit behind flashy scroll pages.

The route itself stays dependency-free. For production client work, these are the patterns and libraries worth reaching for.

MDN / CSS Scroll-driven Animations

Native timelines

Use animation-timeline: scroll() or view() for simple progress bars and reveal effects without measuring scroll in JavaScript.

@supports (animation-timeline: view()) {
  .panel {
    animation: reveal both linear;
    animation-timeline: view();
    animation-range: entry 10% cover 40%;
  }
}
GSAP ScrollTrigger docs

Agency workhorse

For heavy storyboards, ScrollTrigger gives pin, scrub, snap, markers, and timeline composition with less custom geometry code.

gsap.timeline({
  scrollTrigger: {
    trigger: ".chapter",
    pin: true,
    scrub: 1,
    snap: "labels"
  }
});
Lenis README

Smooth scroll foundation

Lenis is built for smooth scrolling, WebGL scroll syncing, parallax, anchors, and modern-browser performance.

import Lenis from "lenis";

const lenis = new Lenis({ autoRaf: true });
lenis.on("scroll", ({ progress }) => syncScene(progress));
Motion for React useScroll docs

React motion values

useScroll exposes scrollY, scrollYProgress, and element offsets; useTransform maps those values to scale, opacity, y, and clip values.

const { scrollYProgress } = useScroll();
const y = useTransform(scrollYProgress, [0, 1], [0, -240]);
return <motion.div style={{ y }} />;
React Three Fiber / Three.js pattern

WebGL layer

When the hero needs liquid metal, particles, depth of field, or camera choreography, put WebGL behind semantic DOM copy.

<Canvas>
  <Scene scrollProgress={progress} />
</Canvas>
// In useFrame: camera.position.z = 8 - progress * 3
IntersectionObserver + requestAnimationFrame

No-dependency baseline

A lean production baseline is observer-gated sections plus one rAF scroll loop that writes CSS variables and lets CSS render.

const progress = clamp(
  (innerHeight - rect.top) / (innerHeight + rect.height)
);
el.style.setProperty("--scene-progress", progress.toFixed(4));

Standing on one foot

Cinematic scroll = sticky scenes + scrubbed progress + depth layers + disciplined fallbacks.

Dimwit take: add parallax. Midwit take: install three animation libraries. Highwit take: storyboard the narrative first, use the browser's native scroll primitives where possible, then pay GSAP/Lenis/WebGL only when the shot demands it.