Bonus Reel ⁄ Production Notes — Feature Nº 09 ⁄ 10

KINETIC

The Title Sequence

The brief: a site that behaves like a film title sequence — Saul Bass by way of GSAP. Type is the only imagery. Scroll is the playhead. Every scene is a full-viewport typographic poster that cuts to the next with a hard palette flip. This page is the director's commentary.

◀ Back to the feature presentation

Reel A ⁄ Palette & Type

ANTON — monumental condensed display. Everything shouted.

TYPE IS THE IMAGERY

MARTIAN MONO — the system voice: timecode, kickers, annotations.

TC 00:01:07:12 · 24 FPS · SC 04 ⁄ INDEX · SCROLL = PLAYHEAD ▸

Reel B ⁄ Ten Techniques

  1. 01

    Scroll = playhead

    One ScrollTrigger spans start:0 → end:"max" and maps document scroll progress onto a 96-second, 24fps SMPTE timecode (00:MM:SS:FF) in the fixed HUD, plus a scaleX playhead bar. The whole page is a timeline being scrubbed.

  2. 02

    Pinned poster scenes

    Each <section class="scene"> gets pin:true with a data-len scroll budget (155–330% of viewport). A scrubbed GSAP timeline per scene runs its choreography; scrub:0.55 gives the playhead a hint of tape inertia.

  3. 03

    Hard palette flips between scenes

    Scenes are transparent in cinema mode; a fixed .backdrop div sits at z-index:-1. For every scene boundary a fromTo tween scrubs backgroundColor across a narrow window (top 72% → top 18%), so cuts feel hard but smear slightly with velocity — like a film splice.

  4. 04

    Build-time split text

    A hand-rolled splitter runs in Astro frontmatter, not in the browser: headlines are server-rendered as word/char <span>s, so no-JS and reduced-motion users get intact text and GSAP gets char handles with zero layout shift. Parents carry aria-label; spans are aria-hidden.

  5. 05

    Outline ↔ fill swaps

    Every display headline is two stacked copies: an outlined base (-webkit-text-stroke, transparent fill) and an absolutely-positioned filled layer clipped with clip-path: inset(0 100% 0 0). Mid-scene the clip wipes to 0 — a typographic reveal with no extra paint cost.

  6. 06

    Velocity-skewed marquees

    Marquee tracks loop with xPercent:-50 repeat:-1. A global ScrollTrigger reads self.getVelocity(), clamps it to ±14deg and quickSets skewX on the tracks (and a softer skewY on the credits roll), decaying back to 0 with power3.out. Scroll hard and the type smears.

  7. 07

    Counter-rotating type rings

    Two SVG <textPath> circles ("OBSESSIVELY CURIOUS • NOMADIC OWLS •") rotate in opposite directions forever; textLength pins the copy to the exact circumference. Scroll velocity bumps their timeScale so they spin up as you scrub.

  8. 08

    Flashcard strobe

    The beliefs scene hard-cuts between five cards with timeline set() calls — and flips the entire backdrop black⁄acid per card, a full-viewport strobe that stays scrub-reversible because every state is a recorded tween.

  9. 09

    Difference-blend HUD

    The timecode HUD and playhead bar are white with mix-blend-mode:difference, so they stay legible over all five palette states without a single media query.

  10. 10

    Reduced motion = poster mode

    gsap.matchMedia() watches prefers-reduced-motion. The default CSS is already a complete stack of static posters (scenes own their background, swap layers pre-filled, cards stacked); motion styles only exist under an .gsap-on class that JS adds when animation is allowed.

Reel C ⁄ Three Ruthless Passes

Pass 1

Inherited a build that compiled clean with zero console errors — but the title-card headline "NOMADIC OWLS" rendered as blank space at 0% scroll. Root-caused with a frame-by-frame GSAP probe: the pre-paint CSS hid the letters via transform:translateY(115%), and GSAP caches an element’s pre-existing transform as a fixed base the first time it touches it, then layers yPercent on top rather than replacing it — so the reveal tween finished at yPercent:0 while the frozen CSS-parsed offset stayed put forever. Also found the outline-accent words inside the belief flashcards ("SEND THEM", "STATIC-FIRST"…) were fully invisible: their CSS referenced currentColor for the stroke color in the same rule that sets color:transparent, which resolves to transparent, not the intended ink color.

Pass 2

Fixed both: swapped the hero’s pre-hide from transform to opacity and folded autoAlpha into its reveal tween (now slides *and* fades, matching every sibling element in the same timeline); gave belief cards a --card-ink custom property so the em stroke resolves correctly in both light and dark tone. Re-screenshotted all 5 desktop depths, 6 mobile depths, the guide route and a prefers-reduced-motion pass. Confirmed two things that looked like bugs at first glance were intentional: the fixed difference-blend HUD crossing over large scene type as it scrolls underneath (by design — a timecode overlay is supposed to read over any footage), and the film-strip sprocket-hole side decoration on the filmography scene.

Pass 3

Final ruthless pass with both fixes in place: re-verified poster quality at every depth, re-ran the reduced-motion fallback (fully static, fully legible, no pins), re-checked mobile for overflow, and swept for any other currentColor/transform-caching footguns elsewhere in the stylesheet (none found — this was the only occurrence of each pattern). Clean build, zero console errors, guide route OK.

Full findings-and-fixes log lives in the repo at _review/kinetic/PASSES.md. No raster assets were used anywhere — the entire feature is type, CSS and one SVG noise tile.

End of Bonus Reel

Designed & built end-to-end by Claude (Fable 5) for Nomadic Owls.

◀ Roll the feature