@MengTo
Scroll experience
✦ClaudeScroll experience
KOISEI sakura river scroll experience (master prompt)
A 19k-character master prompt for an award-level cinematic scroll site. Live at koisei.aura.build.
# MASTER PROMPT — "KOISEI" Sakura River Scroll Experience
Build a single-page, award-level (Awwwards/FWA style) scroll experience called **Koisei** — a cinematic journey down a Japanese river from a sakura morning to a lantern-lit night. Ukiyo-e / woodblock-illustration art direction, WebGL-enhanced imagery, scroll-scrubbed film sequences, and a day-to-night color metamorphosis. Everything below is a hard requirement — follow it exactly.
---
## 1. TECH STACK
- **Vanilla HTML/CSS/JS** (single `index.html` allowed) or Vite project.
- **three.js** (r160+) for: hero petal particle system, WebGL image planes with ripple/displacement shaders, and the transition dissolve shader.
- **GSAP 3 + ScrollTrigger** for all scroll choreography (pinning, scrubbing, timelines).
- **Lenis** smooth scroll (`lerp: 0.08`, `wheelMultiplier: 1.0`), synced to ScrollTrigger via `lenis.on('scroll', ScrollTrigger.update)` and `gsap.ticker`.
- Scroll-scrubbed `<video>` elements driven by `currentTime` (details §8).
- No UI frameworks. No Bootstrap aesthetics. Custom everything.
---
## 2. ASSETS (use these exact URLs)
| Key | Asset |
|---|---|
| `HERO_RIVER` | https://hoirqrkdgbmvpwutwuwj.supabase.co/storage/v1/object/public/assets/assets/sakura-river-2026-07-05/sakura-river-hero-plate-875acd7a5205_original.png |
| `KOI_RIVER` | https://hoirqrkdgbmvpwutwuwj.supabase.co/storage/v1/object/public/assets/assets/sakura-river-2026-07-05/sakura-koi-river-plate-742cc19de6b5_original.png |
| `TEMPLE_CANAL` | https://hoirqrkdgbmvpwutwuwj.supabase.co/storage/v1/object/public/assets/assets/sakura-river-2026-07-05/kyoto-temple-canal-plate-60699d609a45_original.png |
| `KIMONO_LANE` | https://hoirqrkdgbmvpwutwuwj.supabase.co/storage/v1/object/public/assets/assets/sakura-river-2026-07-05/rural-kimono-lane-plate-edb1d9bf1d3d_original.png |
| `LANTERN_WATER` | https://hoirqrkdgbmvpwutwuwj.supabase.co/storage/v1/object/public/assets/assets/sakura-river-2026-07-05/asakusa-lantern-water-plate-73b3e135a98d_original.png |
| `NIGHT_LANTERN` | https://hoirqrkdgbmvpwutwuwj.supabase.co/storage/v1/object/public/assets/assets/sakura-river-2026-07-05/night-river-lantern-plate-510dc7d4315d_original.png |
| `HERO_VIDEO` (optional ambient) | https://hoirqrkdgbmvpwutwuwj.supabase.co/storage/v1/object/public/assets/assets/sakura-river-2026-07-05/sakura-river-bloom-video-5da61fc96d01_video.mp4 |
| `SCROLL_FILM_1` (12s) | https://hoirqrkdgbmvpwutwuwj.supabase.co/storage/v1/object/public/assets/assets/sakura-river-2026-07-05/koi-bridge-temple-scroll-film-12s-ce0df14b7596_video.mp4 |
| `SCROLL_FILM_2` (12s) | https://hoirqrkdgbmvpwutwuwj.supabase.co/storage/v1/object/public/assets/assets/sakura-river-2026-07-05/kimono-asakusa-night-scroll-film-12s-bd5434347f42_video.mp4 |
Preload `HERO_RIVER`, both films' metadata, and fonts behind a loader (§4).
---
## 3. DESIGN SYSTEM
### 3.1 Colors (CSS custom properties — exact values)
```css
:root {
--washi: #F1E9DE; /* primary paper cream (light sections, footer) */
--washi-warm: #ECE4D9; /* hero sky wash */
--washi-deep: #E4DED5; /* gradient partner for washi-warm */
--sand: #E9D4BD; /* card fill / warm accent surface */
--river-stone: #C1BBB2; /* muted water grey-taupe, borders at 40% */
--ink: #251C16; /* primary dark — text on light, night bg */
--night: #241B15; /* night section background */
--night-deep: #1A130E; /* night vignette edge */
--cream-text: #EDE9DF; /* text over imagery / dark bg */
--sakura: #E8B4C0; /* petal pink */
--sakura-deep: #D98E9F; /* petal shadow pink */
--vermilion: #C4472F; /* torii red — accents, hover, small marks only */
--lantern: #E8A54B; /* lantern glow amber — night accents */
}
```
Rules: light sections use `--washi` bg + `--ink` text. Night uses `--night` bg + `--cream-text`. `--vermilion` appears only in tiny doses: the torii logo mark, link hover underlines, section-number tick marks, one CTA. Borders are always 1px `--ink` at 12–15% opacity on light, `--cream-text` at 15% on dark.
### 3.2 Typography
- **Display serif:** `"Cormorant Garamond"` (Google Fonts), weights 400/500 + *italic*. This is the voice of the site.
- **UI sans:** `"Space Grotesk"` 400/500 for nav, kickers, captions, buttons.
- **Japanese accents:** `"Noto Serif JP"` 400 for vertical kanji strips.
Scale (clamp-based, desktop reference):
```css
--display-xl: clamp(4rem, 11vw, 11.5rem); /* hero + footer wordmark */
--display-lg: clamp(3rem, 7vw, 7rem); /* section headings */
--display-md: clamp(2rem, 4vw, 3.5rem); /* overlay lines on films */
--body: clamp(1rem, 1.15vw, 1.185rem);
--caption: 0.8125rem;
--kicker: 0.6875rem; /* letter-spacing: 0.32em; uppercase */
```
Display type: `line-height: 0.95–1.05`, `letter-spacing: -0.015em`, mixed roman/italic within one headline — italicize the emotional word(s) (e.g., "Where Petals ***Drift*** Downstream"). Body: `line-height 1.7`, max-width `34em`, color `--ink` at 78% opacity on light.
Kickers always render as: small vermilion or ink 6px square/tick + 0.32em-tracked uppercase Space Grotesk, e.g. `— THE JOURNEY`.
### 3.3 Texture & grain
- A full-viewport fixed film-grain overlay: WebGL fragment shader or animated SVG `feTurbulence`, opacity 0.05, `mix-blend-mode: overlay`, regenerated per frame (subtle animated noise, not static).
- Every image sits in a frame with a 1px inset border (`--ink` 15%) offset 10px outside the image edge — like a woodblock print mat.
- Soft paper vignette on light sections: radial gradient darkening corners by 3%.
---
## 4. PRELOADER
Washi bg. Center: torii mark (simple 2-stroke SVG in `--vermilion`) above the word **KOISEI** in display serif, tracking-in from `letter-spacing: 0.5em` → `0.02em` (1.2s, `power3.out`). Below, a 1px progress line growing 0→100% width tied to real asset loading, with a percentage counter in Space Grotesk. On complete: line flashes to vermilion, whole loader wipes upward with a `clip-path: inset(0 0 100% 0)` over 0.9s `power4.inOut`, revealing the hero already mid-settle. Total minimum display 1.6s.
---
## 5. NAVIGATION (fixed, persistent)
- Height 88px, full-width, no background initially; padding-inline 48px.
- **Left:** torii SVG mark (22px, vermilion) + wordmark `KOISEI` (Space Grotesk 500, 0.2em tracking, 14px).
- **Right:** links `Journey · Stories · Gallery · Visit` (13px, 0.12em tracking) + a Japanese character `旅` in Noto Serif JP + a pill-outline button `Begin` (1px border, 12px, padding 10px 22px; hover: fills `--ink`, text `--washi`).
- Link hover: 1px vermilion underline draws left→right (transform: scaleX, 0.35s `power2.out`).
- On scroll down > 120px: nav background fades in as `--washi` at 82% + `backdrop-filter: blur(12px)`, height compresses to 68px. When over the night section, swap to `--night` at 82% and cream text (toggle a `.nav--night` class via ScrollTrigger `onToggle`).
- Left screen edge, vertically centered, fixed: a vertical progress rail — 1px line, 160px tall, with a fill tracking total scroll progress and tiny dots at each section; below it the current section index `01 / 07` in 10px Space Grotesk, rotated 90°.
---
## 6. SECTION 1 — HERO ("Where Petals Drift Downstream")
**Layout.** 100svh, full-bleed `HERO_RIVER` rendered as a **three.js textured plane** (not `<img>`) inside a fixed WebGL canvas, covering the viewport with `cover` UV math. Optionally crossfade to `HERO_VIDEO` (muted, loop, playsinline) after load for ambient motion — video as `VideoTexture` at 0 opacity fading to 1 over 2s.
**Type.** Centered, slightly below vertical center:
- Kicker: `— A JOURNEY DOWN THE SPRING RIVER` (cream, 0.32em tracking).
- H1 in `--display-xl`, `--cream-text`, two lines: `Where Petals` / `*Drift* Downstream` (word "Drift" italic). Text has `text-shadow: 0 2px 40px rgba(37,28,22,0.35)`.
- Under it: a thin 1px cream rule 64px wide, then `Koisei — 恋静` caption.
- Bottom center: `SCROLL` label + 40px vertical line with a dot that loops downward (1.8s, `power1.inOut`, infinite).
- Right screen edge: vertical Japanese strip `桜の川を下る` (Noto Serif JP, writing-mode: vertical-rl, cream 60%).
**Entrance (after preloader).** Each headline line wrapped in an overflow-hidden mask; lines rise from `y: 110%` with `rotate: 2deg → 0`, stagger 0.12s, 1.1s `power4.out`. Kicker and rule fade/track-in 0.3s later. WebGL plane starts at `scale 1.12` easing to `1.0` over 2.4s.
**WebGL petal system (signature element).**
- `THREE.InstancedMesh`, 380 instances desktop / 120 mobile, each a small plane (8–18px) with a hand-drawn 5-petal sakura texture (draw it on an offscreen canvas: rounded-heart petal shape, `--sakura` fill, `--sakura-deep` inner shadow).
- Per-instance attributes: size, phase, rotation speed, depth (z: -2 to 2 for parallax).
- Motion in vertex shader or per-frame update: downward drift `vy = 0.15–0.4`, horizontal sway `x += sin(time*0.8 + phase) * 0.3`, plus a **curl-noise flow field** (simplex-based) bending trajectories like wind gusts; tumbling rotation on 2 axes.
- Mouse interaction: pointer position pushes a radial impulse (radius 180px, force falls off quadratically) — petals scatter away from the cursor and recover.
- Petals render OVER the hero image but UNDER the text (canvas z-index between).
- Petal opacity ties to scroll: fade to 0 by the time section 3 begins, re-emerge tinted `--lantern` amber (fireflies/embers, slower, upward drift) in the night section.
**Ripple shader on hero plane.** Fragment shader: subtle vertical sine displacement on the lower 35% of the image (the water) — `uv.x += sin(uv.y*40.0 + uTime*1.2) * 0.0015 * waterMask`, plus mouse-hover ripple: a decaying radial wave from pointer position (`uMouse`, `uWaveTime`).
**Scroll exit (pinned, scrub).** ScrollTrigger pins the hero for `+=120%`. As you scroll: plane scales 1.0 → 1.18 and darkens (shader `uDarken` 0 → 0.25), headline moves up at 1.4× scroll speed and fades, and Section 2 slides over it from below like a paper sheet (Section 2 has higher z-index, `border-radius: 32px 32px 0 0` on its top edge during the overlap, flattening to 0 once fully covering).
---
## 7. SECTION 2 — MANIFESTO ("A River Remembers")
Washi bg. Generous padding (16vh top/bottom).
- Kicker left: `— 01 · THE PHILOSOPHY`.
- A `--display-lg` serif statement spanning ~11 columns of a 12-col grid, `--ink`, mixed italics:
`Every spring, the river carries a *million petals* from the mountains to the sea — and every petal remembers the tree it fell from.`
- **Inline image tokens:** inside this headline, embed two small rounded-rect images (140×90px, border-radius 60px, the `KOI_RIVER` and `TEMPLE_CANAL` crops) sitting on the text baseline between words — they scale in from 0 width as the line reveals (a la modern editorial sites).
- Reveal: split into lines (SplitType or manual), masked line-rise stagger 0.09s triggered at 70% viewport, scrub-free (`toggleActions: "play none none reverse"`).
- Right-aligned below, max-width 34em body paragraph + a text link `Read the story of Koisei →` with draw-on underline.
- Two large numbers row (`1,200 km` / `4 seasons`) in display serif with small caps labels, counter-animating up when in view.
---
## 8. SECTION 3 — SCROLL FILM I (pinned, video scrubbed by scroll)
Full-bleed cinematic chapter using `SCROLL_FILM_1` (12s: koi → bridge → temple).
**Mechanics (exact):**
- `<video preload="auto" muted playsinline>` covering 100svh, `object-fit: cover`.
- Pin the section for `+=400%` scroll. In `onUpdate`: `video.currentTime = self.progress * video.duration`, smoothed through a lerp (`current += (target - current) * 0.1` in a rAF loop) so scrubbing feels buttery, never steppy. Ensure the file is keyframe-dense; if seeking stutters, fall back to a canvas-drawn frame pipeline.
- **Three text chapters** fade in/out sequentially over the film at progress 0.05–0.3, 0.38–0.63, 0.7–0.95. Each: centered kicker (`FIRST BEND`, `UNDER THE BRIDGE`, `THE TEMPLE GATE`) + one `--display-md` cream serif line (e.g., `*Koi* swim against the falling sky`). Text enters with masked rise + slight letter-spacing decay, exits fading up 40px.
- Right edge: a vertical chapter indicator `Ⅰ · Ⅱ · Ⅲ` with the active numeral in vermilion.
- Bottom left: a thin film-style progress bar (1px track, cream fill) showing scrub progress with `00:00 / 00:12` timecode in Space Grotesk tabular numerals.
- Edge treatment: 6% `--night-deep` vignette + the global grain makes it feel filmic.
---
## 9. SECTION 4 — HORIZONTAL GALLERY ("Stations of the River")
Pinned horizontal scroll on `--washi`.
- Header row (static during pin): kicker `— 02 · STATIONS`, heading `Three *stations*, one current` (`--display-lg`), and right-aligned instruction `( scroll )`.
- A track of **3 oversized cards** translating X from `0` to `-(trackWidth - viewport)` with `scrub: 1`, pinned `+=250%`.
- Card spec: width `min(66vw, 900px)`, height 68vh, gap 6vw. Inside: the image fills the card with `overflow: hidden`; image itself is scaled 1.15 and translates X opposite to track motion (inner parallax, ±7%). 1px offset mat border. Corner radius 4px.
- Card 01 — `TEMPLE_CANAL` — `Higashiyama Canal` — caption: `Stone bridges, incense, morning bells.`
- Card 02 — `KIMONO_LANE` — `The Kimono Lane` — caption: `Where the road remembers slower centuries.`
- Card 03 — `LANTERN_WATER` — `Asakusa Waterside` — caption: `Lanterns lean over the current at dusk.`
- Each card's chrome: giant outlined number `01` (display serif, 9rem, `-webkit-text-stroke: 1px var(--ink)`, transparent fill) overlapping the image's top-left corner; title beneath the image in `--display-md`; caption + `Visit station →` link.
- Numbers fill with solid ink when their card crosses viewport center.
- Bottom: 1px progress line for the horizontal track + `01 — 03` counter that swaps with a small roll animation.
- Cursor becomes a custom circular `DRAG`/`→` badge over this section (40px circle, mix-blend-difference).
---
## 10. SECTION 5 — DAY TO NIGHT (the metamorphosis)
The emotional pivot. A pinned 100svh section, `+=200%`.
- Background color animates `--washi` → `--night` via GSAP scrubbing a CSS variable (`backgroundColor` interpolation). The fixed nav flips to `.nav--night`. The global petal system swaps pink→`--lantern` embers drifting **upward**.
- Centered: `LANTERN_WATER` image inside a tall portrait frame (42vw × 78vh) that **scales up to full-bleed** (transform scale + width/height interpolation via GSAP `flip` or clip-path expansion: `clip-path: inset(11vh 29vw)` → `inset(0)`), while a WebGL **dissolve shader** crossfades `LANTERN_WATER` → `NIGHT_LANTERN` using a perlin-noise threshold mask (`smoothstep(uProgress-0.08, uProgress+0.08, noise(uv*4.0))`) so day literally dissolves into night like ink in water.
- Overlay copy at progress 0.1: kicker `— 03 · NIGHTFALL`, line `The river doesn't sleep — it *dreams*` in `--display-lg`, ink color transitioning to cream in sync with the bg.
- Lantern glow: 6–8 positioned radial-gradient sprites (additive blending in the WebGL scene) pulsing gently (`opacity 0.6→0.9`, 3s sine loops, staggered) at the lantern positions of `NIGHT_LANTERN`.
---
## 11. SECTION 6 — SCROLL FILM II (night)
Same mechanics as §8 with `SCROLL_FILM_2` (kimono → Asakusa night), pinned `+=350%`, on `--night`.
- Two chapters: `THE LAST LANE` (`She walks where the lanterns *lean*`) and `ASAKUSA AT NIGHT` (`Every light is a wish going downstream`).
- Ends with a full-stop moment: film settles on its final frame, darkens 30%, and a centered quote fades in — `"恋静 — quiet love, moving water."` with attribution `— Koisei` — held for the last 15% of the pin.
---
## 12. SECTION 7 — FOOTER
On `--washi` (snap back to light with a quick 0.8s bg transition — dawn).
- Top row: kicker `— BEGIN YOUR JOURNEY`, then a giant CTA line `Come see the river *bloom*` (`--display-lg`) with a circular vermilion button (120px, `Begin →`, magnetic hover: translates toward cursor within 24px, springs back `elastic.out(1, 0.4)`).
- Middle: 3 link columns (Journey / Stories / Visit · Instagram / Vimeo / http://Are.na · Contact / Press) in Space Grotesk 13px, hover underline draw; plus a vertical `ありがとう` strip.
- Bottom: the wordmark **KOISEI** at `--display-xl` stretched nearly full-width (`font-size` fitted via JS to viewport width), ink color, sitting on the baseline of the page — it rises from below with a masked reveal as the footer enters, with petals settling on top of the letters (petal system y-velocity → 0 near footer, landing on an invisible floor at the wordmark's cap line).
- Very bottom: 1px rule, `© 2026 Koisei — down the spring river` left, `Kyoto → Tokyo` right, 10px.
---
## 13. GLOBAL MOTION LANGUAGE
- Easing vocabulary: reveals `power4.out`, pinned scrubs linear (`scrub: 1`–`1.5`), color shifts `none` (scrub), micro-interactions `power2.out`, magnetic/elastic `elastic.out(1,0.4)`.
- Every heading reveal = masked line rise (110% → 0, 1–2° rotation settle). Every image reveal = `clip-path: inset(100% 0 0 0)` → `inset(0)` at 1.2s `power4.inOut` with inner image counter-scaling 1.25 → 1.
- Parallax: decorative elements move at 0.85×–1.2× via `data-speed` attributes processed by a single ScrollTrigger.
- Custom cursor: 8px ink dot + 32px trailing ring (lerp 0.15); ring expands to 56px over links, morphs to `→` over cards, `▶` over films.
- `prefers-reduced-motion`: kill pins/scrubs, videos become posters, petals static, reveals become simple fades.
## 14. PERFORMANCE & RESPONSIVE
- One shared `WebGLRenderer` (alpha, antialiasing off, `devicePixelRatio` capped at 1.75); planes/particles added-removed per section visibility. Pause rAF work for offscreen scenes via IntersectionObserver.
- Videos: `preload="metadata"` until their section is within 150% viewport, then `preload="auto"`.
- Mobile (<768px): petals 120, horizontal gallery becomes native swipe track (no pin), film pins shorten to `+=250%`, display-xl clamps down, inline headline images hidden, custom cursor disabled.
- Lazy-decode all plates (`decoding="async"`), and serve the WebGL textures through `THREE.TextureLoader` with `SRGBColorSpace`.
## 15. ACCEPTANCE CHECKLIST
1. Loader → hero entrance feels like one continuous shot.
2. Petals react to the mouse and change character (pink/down → amber/up) across day/night.
3. Both films scrub perfectly smoothly with lerped `currentTime`.
4. Day→night dissolve uses a noise-threshold shader, not a plain crossfade.
5. Horizontal gallery has inner-parallax images and outlined numbers that fill on center.
6. Nav adapts light/dark automatically; progress rail tracks 7 sections.
7. Footer wordmark nearly spans the viewport and petals settle on it.
8. Grain, mat borders, and vignettes present on every section. 60fps on an M1 laptop.