Build log · Origin Run
Brief
A cinematic chapter — the second of the anthology — rendered as the launch page for a fictional trail-running shoe called Origin Run. The aesthetic target was cinematic in the literal sense: scenes that stack and hold attention, a video texture behind the opening, line work that draws itself as you move through the page, and reveal cadences tuned for pacing rather than punch. The reviewer should hit the cinematic card on the landing page, watch a polygon wipe open the new canvas, and feel the chapter settle into its own speed.
Patterns used
clippath-page-transitions— polygon reveal on entrystacked-sticky-scroll-sections— four scenes that stacklazy-video-background— opening scene video texturesvg-path-drawing-scroll— trail profile drawn on scrollstaggered-spring-reveal— spec grid and timeline entries
Type and palette
Cormorant Garamond (a high-contrast display serif) for headings, Inter for body and metadata. Cormorant has the thick-to-thin stroke modulation that reads "cinema" on a poster; Inter is neutral enough to stay out of the way for specs and captions. Palette comes from the chapter wrapper:
[data-chapter="cinematic"] {
--color-bg: #0a0a0a; // near-black
--color-fg: #f4f1ec; // warm paper
--color-muted: #8a8680; // low-contrast label text
--color-accent: #d4b472; // brass metadata
}The chapter is the only dark-theme surface in the anthology. The brass accent is used for scene numbers, the trail path, and the subscribe CTA on future iterations — sparing, never decorative.
Decisions
Four scenes, not five
I drafted five scenes (adding a "why we build in Kentish Town" location scene between manifesto and specs). Cut before shipping. Five stacked sticky scenes puts ~500vh of scroll on the chapter, which exceeds an attention budget I could defend. Four scenes is exactly enough to carry a narrative — hook, argument, object, proof — without feeling like a film-festival edit.
The hero uses LazyVideoBackground, not an always-autoplay <video>
A trail shoe hero could justify a looping 1080p video indefinitely, but the cost is real: ~3–5 MB
over the wire, pause/play state to manage on tab switch, autoplay rejection fallbacks. Our
component uses IntersectionObserver to attach the media source only when the scene is in view,
pauses when it scrolls out, and falls back to an Image poster if autoplay is rejected.
preload="none" on the <video> is load-bearing: without it, the browser starts buffering even
before the src attribute is set. Safari specifically needs playsInline to keep the video from
escaping to fullscreen on iOS; we have it.
overflow: clip, not overflow: hidden
Every stacked scene uses overflow: clip. hidden creates a new scroll container, which silently
breaks position: sticky on ancestors and, much worse, breaks the outer Lenis smooth-scroll
integration without throwing an error. clip clips the paint without introducing the scroll
container. This is the single most common gotcha when building this layout and it is worth
writing down every time.
Spec grid reveals with a spring, timeline with slide-up
Both use the same StaggerReveal primitive, but with different animations. Specs pop from 95%
scale with an overshoot easing (cubic-bezier(0, 0, 0.11, 2.02)) — the numbers arrive confidently.
The timeline entries slide up with the same spring — the dates feel like they're being
uncovered, not announced. Same primitive, two personalities, chosen per scene.
The SVG path is scroll-linked, not load-triggered
DrawPath defaults to scroll-linking the stroke-dashoffset to the element's position in the
viewport. On entry it draws in as you approach; on exit it retracts (if you reverse scroll). I
considered trigger="viewport" — draw once and be done — but the reversibility adds a quiet
feedback signal that the chapter is responding to your movement. The performance cost is a single
passive scroll listener per path; nearly free.
Per-transition opt-in on the landing page
The landing page's cinematic card carries data-transition="clippath-polygon". The provider
registered at the root layout reads the value, picks the polygon keyframes (an 8-point polygon
starting collapsed at centre, expanding to cover the viewport), and runs the animation before
calling router.push. The provider ignores every other link — the editorial chapter has its own
mosaic transition, and ordinary nav links navigate instantly.
Tradeoffs rejected
- A bespoke HLS video pipeline. We ship MP4. HLS would cost a CDN setup and a conditional
hls.jsimport for Safari; the chapter is 90 seconds of ambient footage at most and the adaptive bitrate payoff is not there. - Pinning Scene 1 longer with scroll-scrub video playback. Tempting, but the dominant
tradeoff is accessibility: scroll-scrubbed video is jarring for users with vestibular issues
and impossible to port to
prefers-reduced-motionmeaningfully. Standard autoplay loop, no scroll hijacking. - Replacing the trail SVG with a Lottie file. Lottie would give animated brush-on-brush
detail, but at ~80 KB of JS plus an After Effects export pipeline. A hand-written cubic Bezier
in one
dstring fits the brief with fewer moving parts. - Parallax background on the manifesto scene. Looked lovely in a prototype. In practice it dragged attention away from the sentence we wanted the reader to actually read.
Performance notes
- First-scene LCP is the hero poster image, served through
next/imagewithpriorityandfill. Cold-load LCP on throttled Fast 3G in local testing: ~1.6s. - The video attaches only when Scene 1 is ≥10% in view. Buffered/paused on exit. The
IntersectionObserver uses
threshold: 0.1so the grace zone is generous on both directions. next/fontloads Cormorant and Inter withdisplay: swap. Two font files total for the chapter.StaggerRevealusesIntersectionObserverwithonce: truesemantics — the observer is disconnected from each item after the first reveal. No per-frame work after hydration.- Clip-path animation uses the Web Animations API (
Element.animate), which runs on the compositor. Cheap.
What I'd change in production
- Add a short ambient audio track (muted by default, unmuteable via a corner button) on the hero scene. Trail shoes sell on atmosphere and the site currently does not make a sound.
- Pre-render the trail SVG with commissioned illustration rather than a single cubic curve. A detailed trail map with elevation markers and waypoint labels earns its scene.
- Lift the
ScrollStackto pin Scene 4's CTA for an extra 50% viewport after the last scene lands. Right now the CTA appears and immediately releases; a pin extension would let readers sit with the decision. - Swap the Pexels footage for shot-for-purpose dusk footage. Every launch campaign does; the stock video is a placeholder, not an aesthetic choice.