Capability 07 — Scroll scene

A choreography, scrubbed by scroll.

A single element moves through a multi-keyframe timeline while the page is pinned. Scale, y-position, rotation, opacity, blur, and the background gradient all advance together. Scroll forward and back — the timeline scrubs both directions.

Timeline00%

Composition

A single gesture, scrubbed against the scroll.

Scale, y-position, rotation, opacity, and blur all change together. The background advances through four moods. Scroll up or down and the timeline scrubs both directions — GSAP ScrollTrigger treats scroll as the clock.

Keyframes4PropertiesY, scale, rotation, opacity, blur, bgScrub0.6s smoothing

The code

What’s driving it.

The whole thing is one GSAP timeline with two tracks — card motion and background mood — bound to the scroll position of a pinned section. Scrub smooths it. Keyframes do the rest.

const tl = gsap.timeline({
  scrollTrigger: {
    trigger: section,
    start: "top top",
    end: "+=320%",
    pin: true,
    scrub: 0.6,
    anticipatePin: 1,
  },
});

// Keyframes: 0% → 25% → 70% → 100%
tl.to(card, {
  keyframes: [
    { y: "40vh",  scale: 0.6, rotation: 0,  opacity: 0,   filter: "blur(8px)" },
    { y: "0vh",   scale: 1.0, rotation: -3, opacity: 1,   filter: "blur(0px)" },
    { y: "0vh",   scale: 1.2, rotation: 0,  opacity: 1,   filter: "blur(0px)" },
    { y: "-50vh", scale: 0.9, rotation: 2,  opacity: 0.3, filter: "blur(2px)" },
  ],
  ease: "none",
});

// A second track drives the background gradient through 4 moods.
tl.to(stage, {
  keyframes: MOODS.map((bg) => ({ background: bg })),
  ease: "none",
}, 0);

How this is built

One timeline. Two tracks. Scroll is the clock.

The pin holds the section in view while the user scrolls. Scrub binds the timeline playhead to the scroll position, with a short lag to smooth fast gestures. Keyframes let a single property describe a four-point path without nested tweens.

01ScrollTrigger with scrubscrub: 0.6 gives a 600ms lag that smooths fast scrolls. end: +=320% defines how much scroll distance the timeline occupies.View source
02GSAP keyframes arrayFour keyframes at 0/25/70/100% drive scale, y, rotation, opacity, and blur on one element with one call.View source
03Pin + pinSpacingThe section pins to the viewport while the timeline scrubs. pinSpacing keeps the document height correct so no content collides.View source
04Parallel timeline tracksCard motion and background mood run on the same timeline but start at time 0 via the position parameter — they always stay in sync.View source
05Progress readoutonUpdate pulls self.progress and writes it to a live readout — lets you see the clock position at any time.View source
06gsap.context() teardownThe entire scene is scoped inside a gsap.context so all tweens and ScrollTriggers revert on route change — no leaked pins.View source
07prefers-reduced-motionThe whole timeline short-circuits. The card sits centered at rest. Content is still readable, nothing animates.View source
08ScrollTrigger.refresh() on fonts and loadThe shared gsap-setup helper refreshes pin offsets once fonts and images settle, so the pinned scene doesn't drift on first paint.View source
Back to the showcase