Files
annu-kute-ced/doc2sveltekit-transition/recette-sveltekit-15-sites-awwwards.md
T

909 lines
61 KiB
Markdown
Raw Normal View History

# 15 Award Sites → The SvelteKit Recipe
### An autopsy of Awwwards-grade websites + an actionable implementation guide for Cyber-mawonaj
> **Method note.** Stack claims are grounded in official case studies, Awwwards entries and technical write-ups wherever they exist (footnotes at the end of the document). Where no public source exists (marked *inference*), the analysis is based on observable rendering behavior and the studio's known toolchain. Every "stealable pattern" is something you can extract without the original codebase.
---
# PART 1 — SITE-BY-SITE ANALYSIS
---
## 1. unseen.co — Unseen Studio (Bristol, UK)
Unseen Studio is the rebrand of Green Chameleon, a studio with 35 SOTDs; their own site won SOTD at launch [^38^][^39^].
**[A] Visual language.** Dark, atmospheric base with volumetric lighting as the signature — their designers publish "volumetric lighting studies" as R&D, and that light-fog sensibility pervades the site [^39^]. Palette: near-black grounds, soft chromatic blooms (rainbow-adjacent volumetric glows), restrained UI chrome. Typography is large, confident grotesque with generous tracking; type never competes with the 3D — it floats above it. Composition is free-form asymmetry around a central 3D stage.
**[B] Motion.** Physics-feeling, inertia-heavy. Real-time WebGL particle simulations are an in-house specialty (they teased a "real-time WebGL particle sim" before project launches) [^39^]. Scroll acts as a camera dolly through project worlds; hover states distort or illuminate the medium rather than underline links. Cursor is part of the lighting rig.
**[C] Tech stack.** WebGL/Three.js confirmed by their own hiring and project tags (#webgl #threejs) [^39^]. Custom shader work for volumetrics and particles; DOM reserved for text/UI. Performance strategy: the studio iterates "directly in the browser" with live shader reloading.
**[D] UX.** Scroll-driven portfolio-as-journey. Navigation is minimal; the work *is* the menu. Accessibility trade-off: heavy reliance on pointer + WebGL, text contrast sometimes sacrificed to atmosphere. Mobile gets a simplified but genuine version, not a broken one.
**[E] Critical assessment.**
-**Strengths:** (1) volumetric light identity — instantly recognizable; (2) particle/cursor integration makes the UI feel alive; (3) brand confidence: the site demonstrates the product instead of describing it.
- ⚠️ **Weaknesses:** (1) WebGL-first means a hard floor for low-end devices; (2) atmosphere-over-contrast hurts readability on cheap screens.
- 🔓 **Stealable pattern:** **the cursor-as-light-source** — pass pointer position as a uniform to your scene shader so the user literally illuminates content. One uniform, huge perceived interactivity.
---
## 2. animejs.com — Anime.js v4 (Julian Garnier)
The v4 relaunch site (2025) went viral in r/webdev as "just incredible" — the 3D hero scene is Three.js, everything else is animated with the library itself as a living demo [^14^].
**[A] Visual language.** Deep-space dark UI, electric accent colors, dot-grid/blueprint textures referencing engineering drawing. Typography: technical sans with tabular numerals, kinetic type everywhere — the docs headlines themselves animate. Atmosphere: glassy panels + grain, "lab instrument" aesthetic.
**[B] Motion.** The philosophy *is* the product: timeline-orchestrated, stagger-driven, spring-eased. The v4 API is modular — `animate`, `createTimeline`, `createScope`, `createSpring`, `stagger`, `splitText`, `createDraggable` are separate named imports [^5^]. Scroll Observer API syncs animations to scroll with multiple sync modes and thresholds [^12^]. Micro-interactions: every parameter knob in the docs is draggable and live-updates the demo.
**[C] Tech stack.** Three.js for the hero scene; Anime.js v4 for all DOM/SVG animation [^14^]. Tree-shakeable ESM modules — you import only `animejs/animation`, `animejs/timeline`, etc. [^5^]. This modularity is itself a lesson: ship ≤ what you use.
**[D] UX.** Documentation as experience: interactive playground embedded in the narrative. Accessibility: motion-heavy but each demo is self-contained; reduced-motion handling is left to the implementer (a gap).
**[E] Critical assessment.**
-**Strengths:** (1) product demonstrates itself — zero stock imagery; (2) interactive docs collapse learning time; (3) spring physics defaults feel modern vs. GSAP's classic easings.
- ⚠️ **Weaknesses:** (1) animation overload can fatigue on long doc sessions; (2) no first-party reduced-motion story.
- 🔓 **Stealable pattern:** **`createScope` per component** — scope animation queries to a component root and `revert()` on cleanup. Maps 1:1 to Svelte's component lifecycle (see §6).
---
## 3. immersive-g.com — Immersive Garden (Paris)
3× Awwwards Studio of the Year, 90+ awards; the 2025 rebuild won SOTM and is fully documented in an official case study [^24^][^27^].
**[A] Visual language.** Minimalist-yet-atmospheric: bas-relief 3D compositions of natural elements, Roman numerals rendered in 3D as menu anchors, muted stone/bone palettes with deep shadows [^27^]. It's "digital craftsmanship" as brand — tactile, sculptural, almost geological.
**[B] Motion.** Restrained and ceremonial. GSAP for transitions, **Lenis for scroll** [^27^]. Nothing bounces; everything glides. The wow is in scene transitions and reveal choreography, not jitter.
**[C] Tech stack (confirmed):** Three.js + Blender/Houdini/ZBrush asset pipeline; **Vue/Nuxt frontend; GSAP + Lenis; Strapi + Node backend; Vercel deployment** [^27^]. Performance strategy is the headline: **server-side KTX GPU texture compression, channel-packed textures, gltf-transform automated exports** with custom Blender/JS scripts [^27^].
**[D] UX.** One-click access navigation doctrine: any case study reachable in a single interaction. The **"Backstage" section** — behind-the-scenes technical breakdowns per project — is a hidden-gem IA pattern that turns process into content [^27^].
**[E] Critical assessment.**
-**Strengths:** (1) compression pipeline is industry best practice (KTX + channel packing); (2) Backstage = transparency as marketing; (3) minimalism with one strong sculptural motif instead of ten effects.
- ⚠️ **Weaknesses:** (1) Strapi + Node backend adds hosting weight you'd feel on a 50 €/mo budget; (2) realism-first 3D = large asset payloads even compressed.
- 🔓 **Stealable pattern:** **the Backstage section** — document your process publicly per project. For Cyber-mawonaj this doubles as pedagogical content (open knowledge = sovereignty).
---
## 4. davidwhyte.com/experience — David Whyte Experience (by Immersive Garden)
SOTM Dec 2024; official case study available [^17^][^20^].
**[A] Visual language.** Watercolor paintings by Matthew Phinn as the entire visual substrate — organic pigment bleeds, paper texture, muted landscape hues. Typography is literary serif, quiet. This is the anti-neon site: proof that "immersive" doesn't mean cyber.
**[B] Motion.** Two hero techniques: a **cursor-driven watercolor fluid simulation** — one fluid sim per visible painting, optimized via a **simulation atlas + stencil buffer** so only active paper regions compute [^20^]; and a **generative watercolor reveal** — noise-layered droplets dissolving the painting into view, with **noise baked in advance** to avoid real-time cost [^20^]. Long-press reveals video of the real location.
**[C] Tech stack (confirmed):** Nuxt/Vue, Sass + BEM, Three.js (Blender-driven camera recreated in WebGL, custom shaders), GSAP, Lenis, WordPress CMS, Vercel [^20^].
**[D] UX.** Scroll through landscapes; each poem gets full-screen focus. Long-press is a deliberate slow-interaction — forces contemplation, matches the content's tempo. Accessibility: text remains real DOM text (good); the fluid sim is decorative-only (also good).
**[E] Critical assessment.**
-**Strengths:** (1) baked-noise reveal = generative feel at zero runtime cost; (2) stencil-buffer atlas is a genuinely clever perf pattern; (3) motion tempo matched to content (slow poetry, slow interaction).
- ⚠️ **Weaknesses:** (1) fluid sim still taxes low-end GPUs; (2) long-press is undiscoverable without onboarding hints.
- 🔓 **Stealable pattern:** **bake your noise** — precompute generative textures offline (Blender/Python/ComfyUI for you), ship them as KTX2, blend in shader. Generative look, static-asset perf. Perfect fit for your ComfyUI pipeline.
---
## 5. kriss.ai/home/aftercare — Kriss.ai (by Studio 28K)
Awwwards case study available [^28^].
**[A] Visual language.** Deliberate rejection of "cold AI tech" aesthetics: **70s-inspired palette, organic shapes, soft warm tones** — a dental AI presented as a human service [^28^]. The whole site is a "Dollhouse": an open 3D cutaway of a dental clinic where each room (front desk, surgery, aftercare) is a content section.
**[B] Motion.** Spatial navigation through the dollhouse: camera moves between rooms on scroll/click. Motion serves wayfinding — you always know where you are in the building, hence in the offer.
**[C] Tech stack (confirmed):** WebGL 3D built in Blender; **deployed on Cloudflare Pages + R2 for media** — a notable choice: heavy visual assets on an edge CDN, zero traditional backend [^28^]. Figma → Blender → VSCode pipeline.
**[D] UX.** Metaphor-driven IA: product features mapped to physical rooms. This collapses the learning curve for non-technical buyers (dentists) — the spatial metaphor carries the explanation.
**[E] Critical assessment.**
-**Strengths:** (1) narrative pivot "from tech to human" executed in the medium itself; (2) dollhouse = site map you can see; (3) **Cloudflare Pages/R2 architecture proves you can host heavy WebGL at near-zero backend cost** — directly relevant to your budget.
- ⚠️ **Weaknesses:** (1) the 3D metaphor forces all content into rooms — awkward for dense text; (2) 70s warmth risks reading as dated outside the US market.
- 🔓 **Stealable pattern:** **the dollhouse IA** — map AKILPA's or Cyber-mawonaj's sections to physical Caribbean spaces (case à ka, marché, karbay, plage). Navigation becomes cultural storytelling. Free pattern, no shaders required.
---
## 6. zentry.com — Zentry (by Resn)
Full Awwwards case study available — and the tech decision is the most instructive of all 15 sites [^6^].
**[A] Visual language.** Bold, vibrant, "evil-corporation-playful": saturated color fields, chunky display type, 3D portal masks as signature motif. Sound-designed: dynamic soundtrack evolving from upbeat to ambient as you explore [^6^].
**[B] Motion.** Motion system designed **before** the website: every reaction, curve and principle specced upfront [^6^]. Flashy, fast, reactive — but (see below) done entirely in DOM.
**[C] Tech stack (confirmed):** **Nuxt/Vue + Vite, proprietary utility framework — and deliberately NO WebGL.** The team needed massive DOM throughput (text, dynamic media) and chose to build the 3D portal-mask effects as **custom DOM/CSS solutions instead of Three.js**, explicitly to minimize complexity and maximize DOM performance [^6^].
**[D] UX.** Portal motifs as content reveals; sound as orientation layer. The lesson: "immersive" is a design outcome, not a rendering technology.
**[E] Critical assessment.**
-**Strengths:** (1) **proof that award-grade "3D" can be pure DOM** — transform3d, clip-path, masks, perspective; (2) motion-first design process (curves before comps); (3) sound design treated as first-class UX.
- ⚠️ **Weaknesses:** (1) proprietary framework = not reproducible as-is; (2) audio autoplay remains an accessibility/UX minefield.
- 🔓 **Stealable pattern:** **DOM-only portal masks**`clip-path` + `transform-style: preserve-3d` + a GSAP scrub timeline gives you 80% of the Zentry wow at 5% of the GPU cost. **This is your #1 pattern for 4G/low-end Gwada devices.**
---
## 7. igloo.inc — Igloo Inc (by Abeto + Bureaux)
SOTD Jul 2024 (7.92/10); unusually detailed case study + WebGPU showcase write-up [^3^][^7^][^1^]. **And it's built in Svelte — your stack.**
**[A] Visual language.** A frozen landscape: each portfolio project encased in a **procedurally generated ice block** (custom crystal-growth algorithm inside container shapes — unique every time, scales without manual modeling) [^1^]. Chromatic aberration + frost-dissolve scene transitions; intro sequence "between sci-fi title crawl and luxury brand ad" [^1^].
**[B] Motion.** Camera drifts between scenes; footer is an interactive particle simulation that coalesces into different 3D shapes per hovered link, **color shifting with particle velocity** [^1^]. Text glitches via shaders; **letter-scramble swaps SDF texture offsets instead of forcing DOM relayout** — a performance decision doubling as a creative one [^1^].
**[C] Tech stack (confirmed):** **Three.js + Svelte + GSAP**, Houdini + Blender, plus proprietary tooling including a **custom VDB-to-browser exporter** compressing volume data smaller than a typical website image [^1^]. Entire UI rendered in WebGL. Workflow: grey-box previs animations, then iterate directly in browser with real-time shader/model reloading, continuously measuring on low-end devices [^7^].
**[D] UX.** Three sections only — the constraint forced a scroll experience dense with interaction [^7^]. Navigation = the journey itself.
**[E] Critical assessment.**
-**Strengths:** (1) **Svelte at the absolute top tier of the craft** — kills any "can Svelte do this?" doubt; (2) procedural assets scale content without artists; (3) SDF-text-in-shader eliminates DOM relayout cost.
- ⚠️ **Weaknesses:** (1) full-WebGL UI = screen-reader hostile, needs a parallel DOM layer; (2) proprietary VDB pipeline is not reproducible on a budget.
- 🔓 **Stealable pattern:** **velocity-reactive particles** — map particle/element color or size to pointer *velocity*, not just position. Trivially implementable in a Svelte action (`$derived` from delta of pointer positions), huge tactile payoff. Also: **SDF text rendering** if you go WebGL-UI (troika-three-text does this for free).
---
## 8. kprverse.com — KPR (by Resn)
SOTD Dec 2022 (7.98) + SOTM; official making-of available [^35^][^45^].
**[A] Visual language.** "New Eden" concept-art tableaux: hand-painted assets, bold concept-art style, terminal/console easter-egg aesthetics (fake file-system loading messages, "HTTPS://KPRVERSE.COM/KPCO/..." loader strings) [^41^]. Mixes 3D characters with 2D design elements, "interactive motion revealing a hidden dimension" [^45^].
**[B] Motion.** **Click-and-hold** as the signature: holding reveals a second interactive layer (geography, factions, lifestyle of the world) [^45^]. Scroll moves through parallax tableaux (the Keep, the Factions, the World). Loading is theatrical: console-style progress theater.
**[C] Tech stack.** Resn proprietary (Nuxt-era Resn = Vue-based, per their Zentry write-up [^6^]); layered 2D/3D compositing rather than full 3D scenes.
**[D] UX.** Narrative gating: lore revealed in layers, community invited to "shape the story". Notably **hard device gating**: "Your Browser resolution is currently not supported" and portrait-only enforcement on mobile [^41^] — a controversial but deliberate trade.
**[E] Critical assessment.**
-**Strengths:** (1) click-and-hold "second layer" is a brilliant lore-delivery mechanic; (2) loader-as-fiction (terminal theater) turns wait time into worldbuilding; (3) tableaux = content with room for community contribution.
- ⚠️ **Weaknesses:** (1) **resolution gating is hostile** — for Gwada's mobile-dominant reality this pattern is toxic; ship a degraded mode instead; (2) hand-painted asset volume = production cost.
- 🔓 **Stealable pattern:** **click-and-hold reveal layer** — pointer-down starts a GSAP timeline that cross-fades a hidden info stratum; release reverses it. One timeline, two states, infinite narrative uses (patrimoine layers: colonial map ↔ créole map).
---
## 9. hape.io — HAPE (by Dogstudio/DEPT for Digimental)
SOTD Apr 2022 (7.84, dev award animations 9.4/10); official case study available [^36^][^37^][^40^].
**[A] Visual language.** High-fashion-magazine web design: black/gold footer palette, editorial grids, the 3D ape treated like a couture model [^36^][^37^].
**[B] Motion.** **The HAPE walk is the scroll**: page opens zoomed on the ape's head; scrolling rotates the camera and dollies out to reveal the full strutting character — a walk cycle art-directed for "attitude" and **synchronized to a handpicked soundtrack** [^40^]. A "hapewalk mode" hands rotation/zoom control to the user [^40^].
**[C] Tech stack (confirmed):** **custom OGL micro-framework** — OGL (minimal-abstraction WebGL library) extended in-house with PBR rendering, skinning, post-processing and an animation player [^37^]. Built in 4 weeks [^37^].
**[D] UX.** One character, one gesture (scroll), one attitude — radical focus. Results: 57K visitors launch month, 4.58 min average session [^37^].
**[E] Critical assessment.**
-**Strengths:** (1) scroll-synced skeletal animation with music sync is a masterclass in single-idea execution; (2) OGL proves you don't need Three.js if your needs are narrow; (3) fashion-editorial framing elevates NFT kitsch to design.
- ⚠️ **Weaknesses:** (1) skinned 3D + PBR is heavy for low-end mobile; (2) audio-synced motion degrades silently when sound is off/blocked.
- 🔓 **Stealable pattern:** **scroll-scrubbed skeletal camera** — bind `scrollProgress` to a baked animation clip's time (`mixer.setTime(progress * duration)`). Works with any GLTF walk/dance cycle. Imagine it with a **gwoka dancer** or a lambi procession: cultural motion as scroll engine.
---
## 10. labs.noomoagency.com — Noomo Labs
Developer Award + SOTD 2024; official behind-the-scenes available [^16^][^13^].
**[A] Visual language.** A dreamlike underwater journey with a glass jellyfish: 3D glass materials, refractive transparency, calm ambient sound design ("sound of the glass sphere appearing", music complementing the jellyfish) [^13^]. It's a hub for experiments — R&D made public.
**[B] Motion.** The technical doctrine: **all 3D assembled in ONE Blender scene; all non-interactive animation (camera, jellyfish, words) baked in Blender; materials baked to textures** [^13^]. Camera rigged to two Empties — one at the focal point, one animating the camera object [^13^]. Runtime = playback + interactive garnish.
**[C] Tech stack (confirmed):** Three.js + GSAP, **explicitly avoiding React Three Fiber "to maintain flexibility"**, Nuxt framework, Blender-baked assets [^13^]. The storytelling sister site scrolls a Three.js camera through five differently-lit "rooms" of case studies [^8^].
**[D] UX.** Labs = experiments before they "grow up" [^8^] — an institutional pattern: public R&D as marketing.
**[E] Critical assessment.**
-**Strengths:** (1) **bake-everything philosophy = cinematic quality at playback cost**; (2) one-scene assembly avoids runtime scene-graph juggling; (3) labs-as-content is a sustainable publishing model.
- ⚠️ **Weaknesses:** (1) baked = rigid; content changes require re-bakes; (2) glass/refraction shaders are expensive on Mali-class GPUs.
- 🔓 **Stealable pattern:** **the two-Empty camera rig** — in Blender, parent your camera to a target-Empty + path-Empty, bake, export GLTF, and in SvelteKit just scrub `cameraAnimation.time` from Lenis scroll progress. Zero camera math at runtime. Your ComfyUI/Blender pipeline already supports this.
---
## 11. noomoagency.com — Noomo Agency
Awwwards **Website of the Year** winner [^9^][^16^].
**[A] Visual language.** Glassmorphism at its most disciplined: glass cards on-scroll animations, 3D hover effects, a **dynamic logo incorporating handwritten elements from each team member** — personalization as brand system [^9^][^11^]. Boutique-agency warmth inside high-tech execution.
**[B] Motion.** "Every interaction and hover has its unique purpose" [^9^] — hover states are individually art-directed (button hovers, 3D case previews, showreel controls) [^11^]. Scroll animations drive case reveals; 3D transitions between pages.
**[C] Tech stack (confirmed):** **Nuxt 3 front AND back (Node), Three.js + GSAP** [^9^].
**[D] UX.** Classic agency IA (work/story/contact) elevated by transition choreography — proof you don't need experimental IA if execution is flawless.
**[E] Critical assessment.**
-**Strengths:** (1) hover-as-portfolio: micro-interactions individually designed, not defaulted; (2) humanizing details (handwritten logo) offset tech flex; (3) full-stack Nuxt keeps ops simple.
- ⚠️ **Weaknesses:** (1) glass everywhere = blur = GPU cost on weak devices; (2) 3D page transitions can delay content access.
- 🔓 **Stealable pattern:** **budgeted hover craft** — pick your 5 most-tapped elements and give each a unique, purposeful micro-interaction; leave the rest at system defaults. Award juries notice the ratio of intentional to generic interactions.
---
## 12. nudot.com.tw — Nudot (Taiwan)
*No public case study found — this section is inference from the live site and studio profile; treat stack claims as low-confidence.*
**[A] Visual language.** Taiwanese studio aesthetic in the lineage of Ultra Combos/Studio Pros: precise CJK+Latin bilingual typography, generous whitespace punctuated by abrupt kinetic moments, muted palette with one aggressive accent. Bilingual typesetting (Chinese vertical/horizontal + English) is itself the design flex.
**[B] Motion.** Text-led motion: character-level reveals, marquee systems, cursor-following media previews on project lists. Motion is typographic rather than 3D.
**[C] Tech stack (inference).** DOM/CSS + GSAP-class animation, canvas accents; likely Nuxt or similar Vue meta-framework (dominant in Taiwanese award studios).
**[D] UX.** Project-list-as-gallery with hover media previews; bilingual toggle as first-class nav item — relevant precedent for FR/KRÉ bilingual switching.
**[E] Critical assessment.**
-**Strengths:** (1) bilingual typography treated as design material, not i18n afterthought; (2) restraint — effects deployed sparingly; (3) fast: typography-led sites are inherently light.
- ⚠️ **Weaknesses:** (1) hover-previews have no touch equivalent (mobile gets a lesser list); (2) minimal feedback cues can confuse first-time visitors.
- 🔓 **Stealable pattern:** **bilingual kinetic type** — design your FR/KRÉ switch as an *animation event* (lines rotate/mask-swap in place) rather than a page reload. Language toggle becomes brand moment. Implement with `splitText` + a shared-layout transition.
---
## 13. 375.studio/en/studio — Studio375 (Vicenza, Italy)
SOTD Apr 30, 2026 (7.33, plus Developer Award) [^2^][^15^].
**[A] Visual language.** Two-color discipline (their earlier HM site was strict #000/#FFF) [^10^], clean-flat-minimal portfolio aesthetic with colorful punctuations [^10^]. Communications-agency clarity rather than tech-lab showing-off.
**[B] Motion.** Hero image animation, mouse interaction, scroll-linked text decoration, animated 404 — a **complete system of small moments** rather than one big WebGL bet [^10^]. Dev-award scores show the craft is in transitions (animations 7.8, their strongest dev metric) [^2^].
**[C] Tech stack (inference).** DOM-first, GSAP-class orchestration; no heavy WebGL signature. Accessibility dev-score 7.2 is above the award-site average [^2^] — suggests semantic care.
**[D] UX.** Conventional studio IA executed with transition polish; even the 404 is a designed moment [^10^].
**[E] Critical assessment.**
-**Strengths:** (1) proves SOTD is achievable without WebGL; (2) systematic micro-motion (every page state designed, incl. errors); (3) strong accessibility score for the category.
- ⚠️ **Weaknesses:** (1) less memorably distinctive than the WebGL cohort; (2) creativity scores (7.57) lag the experimental sites.
- 🔓 **Stealable pattern:** **design your error/empty/404 states with the same rigor as the hero.** For an association site (AKILPA), the 404, the "no events yet", the offline page are touchpoints where grassroots personality can outshine budget.
---
## 14. ten.375.studio/en — "Ten Years Away" (Studio375)
SOTD Jun 26, 2026 (7.32; users rated creativity 9.3) [^4^].
**[A] Visual language.** **An interactive comic** chronicling ten years of the studio — "real characters, real stories" [^4^]. Illustrated panels, comic-page composition translated to scroll, hand-drawn texture over digital precision.
**[B] Motion.** Mouse trail as a signature element, page transitions between comic chapters, intro sequence [^4^]. Motion serves panel-to-panel reading rhythm — scroll as page-turn.
**[C] Tech stack (inference).** DOM/SVG illustration + GSAP-class scrubbing; the comic format suggests asset sequencing rather than runtime 3D.
**[D] UX.** Narrative chronology as IA: ten years = ten chapters. Personal/historical storytelling instead of service listing — an anniversary site that earns attention through vulnerability (real people, real stories).
**[E] Critical assessment.**
-**Strengths:** (1) format courage — a comic, not a portfolio grid; (2) chaptered narrative maps perfectly to scroll; (3) authenticity as differentiator (users scored creativity 9.3, far above jury [^4^]).
- ⚠️ **Weaknesses:** (1) niche payoff — low utility for prospective clients; (2) illustrated asset production is time-expensive.
- 🔓 **Stealable pattern:** **the chaptered scroll-comic** — for AKILPA, a "10 dates du patrimoine gwada" or "l'histoire du lambi" as an interactive scroll-comic. Chapters = routes (`/histoire/[slug]`), panels = SVG/AI-generated assets (your Qwen/Seedream pipeline), transitions = shared-element morphs. High storytelling ROI per byte.
---
## 15. apple.com/airpods-pro — Apple
The canonical scroll-scrubbed product page; technique fully documented by CSS-Tricks and the GSAP community [^19^][^18^].
**[A] Visual language.** Product-photographic maximalism on pure black; type appears only when the product pauses. No texture tricks — the hardware render *is* the visual language.
**[B] Motion.** **Scroll-scrubbed image sequences**: a 148-frame JPEG flipbook drawn to a fixed `<canvas>`, frame index = scroll fraction × frame count [^19^]. Content sections are one big master timeline with percentage-hand-picked timings; "fake pins" hold content in place [^18^]. requestAnimationFrame for tear-free frame swaps [^19^].
**[C] Tech stack (documented technique).** Canvas 2D + image preloading; no WebGL needed [^19^]. **Performance doctrine: on slow connections Apple ships a single fallback image instead of the sequence** — and offers smaller sequences per device class [^19^].
**[D] UX.** Scroll = product film. Information hierarchy: specs arrive only after desire is built. Accessibility: canvas content is mirrored by real DOM text below.
**[E] Critical assessment.**
-**Strengths:** (1) the flipbook is the most copied award pattern alive — simple, robust, GPU-cheap; (2) **explicit slow-network fallback strategy** — exactly your 4G doctrine; (3) master-timeline percentage timing keeps everything in sync across viewports [^18^].
- ⚠️ **Weaknesses:** (1) 148 frames is a data hog without fallbacks; (2) canvas imagery needs parallel DOM for a11y/SEO.
- 🔓 **Stealable pattern:** **the scroll flipbook with connection-aware loading**`navigator.connection.effectiveType` decides: full sequence / reduced frame set / single hero image. Ship it as a Svelte action (see §5, bonus component). **This is the single most Gwada-appropriate award pattern in this list.**
---
---
# PART 2 — THE SVELTEKIT RECIPE
## 1. Core Principles (the common DNA)
**Principle 1 — Scroll is the primary navigation driver.** 13 of 15 sites bind narrative progression to scroll. Canonical stack: **Lenis + GSAP ScrollTrigger, wrapped as Svelte actions/context** (Immersive Garden ships exactly this combo [^20^][^27^]). In SvelteKit: one Lenis instance in the root layout, exposed via context; never instantiate per component.
**Principle 2 — Bake at build, not at runtime.** David Whyte bakes noise [^20^]; Noomo bakes camera, materials and character animation in Blender [^13^]; Immersive Garden compresses textures server-side to KTX [^27^]; Igloo compresses volumes to sub-image size [^1^]. Rule: **if a value doesn't depend on live user input, it has no business being computed in the browser.** Your ComfyUI/Blender pipeline is your "baking" layer — treat it as part of the build system.
**Principle 3 — DOM first, WebGL only where it pays.** Zentry did its award-winning "3D" portals in pure DOM deliberately [^6^]; Apple's flipbook is Canvas 2D [^19^]. Reserve WebGL for: particle systems, fluid sims, real 3D assets with user-controlled cameras. Everything else: CSS transforms, clip-path, SVG. **For 4G Guadeloupe this principle is non-negotiable.**
**Principle 4 — Motion is designed before pages.** Zentry specced its motion system before design started [^6^]; HAPE art-directed a walk cycle, then built a site around it [^40^]. Define your easing vocabulary, durations and rhythm (for you: gwoka cadences — see Part 3) as **design tokens**, before any component exists.
**Principle 5 — Every state is a designed moment.** Studio375's animated 404 [^10^], KPR's terminal-theater loader [^41^], Noomo's per-element hover craft [^9^]. Loading, error, empty, offline: these are where personality lives when budget doesn't.
---
## 2. SvelteKit Architecture Patterns
### 2.1 Route structure for immersive experiences
```
src/routes/
├── +layout.svelte ← SmoothScrollProvider (Lenis) + CursorTracker live HERE
├── +layout.ts ← export const ssr = true (default)
├── (site)/ ← layout group: classic content pages
│ ├── +page.svelte ← home (light hero, DOM effects)
│ ├── histoire/[slug]/ ← chaptered scroll-comic (Ten Years Away pattern)
│ └── patrimoine/[slug]/ ← case studies, "Backstage" pattern (Immersive Garden)
├── (immersive)/ ← layout group: canvas-heavy experiences
│ ├── +layout.svelte ← WebGLCanvas provider, ssr disabled per-page
│ └── mawonaj/+page.svelte
└── +error.svelte ← designed 404 (Principle 5)
```
Rules:
- **Layout groups** isolate the WebGL context to the routes that need it — the canvas provider never mounts on content pages.
- **Dynamic imports** for anything heavy: `const { WebGLCanvas } = await import('$lib/webgl/WebGLCanvas.svelte')` behind `{#await}` or `{#if browser}` gates.
- **Progressive enhancement**: every immersive route must render meaningful SSR HTML underneath the canvas (Apple mirrors its canvas with DOM text [^19^] — do the same).
### 2.2 Where WebGL lives
Use **Svelte context**, created once in the `(immersive)` layout:
```ts
// $lib/webgl/context.ts
import { setContext, getContext } from 'svelte';
import type * as THREE from 'three';
export interface WebGLContext {
renderer: THREE.WebGLRenderer;
scene: THREE.Scene;
camera: THREE.PerspectiveCamera;
canvas: HTMLCanvasElement;
}
const KEY = Symbol('webgl');
export const provideWebGL = (ctx: WebGLContext) => setContext(KEY, ctx);
export const useWebGL = () => getContext<WebGLContext>(KEY);
```
Child scenes register/unregister in `$effect` cleanup. **One renderer per app, many scenes.** Never two `<canvas>` WebGL contexts on one page for low-end targets.
For Three.js in Svelte, evaluate **Threlte** (the Svelte-native Three layer) for standard scenes, and raw Three.js for custom shader-heavy work (Igloo-style). Both coexist: Threlte components render into your single context.
### 2.3 Animation orchestration
| Need | Tool | Why |
|---|---|---|
| Component enter/leave, list transitions | **Svelte built-in transitions** (`transition:`, `in:`/`out:`) | Zero deps, SSR-safe, cancellable |
| Scroll-scrubbed timelines, pinning | **GSAP + ScrollTrigger** | Industry standard on 11/15 sites |
| Lightweight springs, draggable, text splitting | **Anime.js v4** (modular imports) [^5^] | ~5× smaller import surface when tree-shaken; `createScope` maps to component lifecycle |
| Physics cursor / spring values | Anime.js `createSpring` or a 12-line custom spring | No GSAP needed for this |
| One shared clock | GSAP `ticker` as the single rAF driver, Lenis hooked into it | Prevents competing rAF loops |
---
## 3. CSS Architecture for Award Sites
### 3.1 Tailwind + CSS variables for dynamic theming
```css
/* app.css — the theme lives in variables, Tailwind references them */
:root {
/* Cyber-mawonaj tokens (see Part 3) */
--lagune-phospho: oklch(72% 0.13 210);
--roche-volcan: oklch(18% 0.02 260);
--corail: oklch(70% 0.16 25);
--or-circuit: oklch(80% 0.14 90);
--foret-neon: oklch(55% 0.15 155);
--ease-ka: cubic-bezier(0.22, 1, 0.36, 1); /* motion tokens too */
--dur-mesure: 480ms;
}
@theme inline {
--color-lagune: var(--lagune-phospho);
--color-volcan: var(--roche-volcan);
/* … */
}
```
Theme switching = swapping a `data-theme` attribute on `<html>`, variables cascade, Tailwind utilities follow. No JS class churn, no FOUC if set in `%sveltekit.head%`.
### 3.2 Container queries > media queries for components
Media queries for page-level breakpoints; **container queries for every reusable component** (cards, chapters, nav). Award sites break layouts constantly because components are viewport-coupled; container queries make your `KineticText` or `ParallaxLayer` correct in a sidebar, a dialog, or a split-screen dollhouse room.
### 3.3 CSS that replaces JS
- **`animation-timeline: scroll()` / `view()`** — native scroll-driven animations for parallax and reveals. ⚠️ *Baseline 2023+: Chrome/Edge/FF OK; Safari 26+ only — keep a ScrollTrigger fallback for Safari < 26 (still common in Gwada's device pool).* Flag: **needs progressive enhancement wrapper.**
- **`scroll-snap`** for chaptered comics (Ten Years Away pattern) — free, native, accessible.
- **`:has()`** for stateful styling (hovered card dims siblings) instead of JS class toggles.
- **View Transitions API** for route morphs (see PageTransition, §5). ⚠️ *Chrome/Edge/Safari 18+; Firefox behind flag — design as enhancement.*
---
## 4. Performance Budget & Optimization
### 4.1 Budgets (mobile 4G, low-end Android — the Gwada baseline)
| Resource | Budget | Rationale |
|---|---|---|
| JS initial (compressed) | **≤ 170 KB** | ~1.7 s parse on entry-level 4G Android |
| WebGL textures | ≤ 8 MB GPU RAM | use **KTX2/BasisU**, never raw PNG to GPU [^27^] |
| Models | ≤ 500 KB per scene | Draco or meshopt via gltf-transform [^27^] |
| Hero media | ≤ 400 KB before interaction | poster + lazy sequence (Apple doctrine [^19^]) |
| Total page weight (home) | ≤ 2 MB | reality check against award sites' 8-15 MB |
| Frame budget | 16 ms desktop / **target stable 30 fps on Mali-G52** | test on a real ~150 € Android, not DevTools throttling |
### 4.2 WebGL asset pipeline
```
Blender → gltf-transform CLI:
gltf-transform optimize in.glb out.glb \
--compress meshopt --texture-compress ktx2
```
- **KTX2 + UASTC/ETC1S** textures (Immersive Garden's exact strategy [^27^]); channel-pack roughness/metallic/AO into one texture [^27^].
- **LOD**: two model tiers; pick at runtime from `renderer.capabilities` + `deviceMemory`.
- **Instancing** for repeated elements (lambi shells, vegetation, stars).
- **Bake lighting** into textures wherever the camera path is fixed (Noomo doctrine [^13^]).
### 4.3 Animation frame budget
Concurrent JS-driven animations before jank on low-end: **~8-12 transform/opacity tweens**. Beyond that: batch into one timeline, move to CSS, or cull off-screen animations (`ScrollTrigger` with `toggleActions`, or IntersectionObserver gating). Animate **only `transform` and `opacity`** — layout properties cause reflow (Anime.js v4 docs say the same [^5^]).
### 4.4 SSR vs CSR for canvas pages
- Canvas/WebGL pages: **SSR the semantic content shell, hydrate the canvas client-side** (`{#if browser}` + dynamic import). Don't disable SSR route-wide unless the page is truly content-free — you lose SEO and slow-connection resilience for nothing.
- Lenis/GSAP init: `onMount` only, always.
- SvelteKit prerendering (`export const prerender = true`) for everything static → perfect on Cloudflare Pages/Netlify free tiers, mirroring Kriss.ai's edge-CDN architecture [^28^].
---
## 5. Component Library — Reusable Svelte 5 Modules
All components: Svelte 5 runes syntax, TypeScript, reduced-motion aware, cleanup on destroy.
### 5.1 SmoothScrollProvider — Lenis as layout-level context
```svelte
<!-- $lib/components/SmoothScrollProvider.svelte -->
<script lang="ts">
import { onMount, setContext, type Snippet } from 'svelte';
import Lenis from 'lenis';
import gsap from 'gsap';
let { children }: { children: Snippet } = $props();
let lenis: Lenis | null = $state(null);
setContext('lenis', { get: () => lenis });
onMount(() => {
const reduced = matchMedia('(prefers-reduced-motion: reduce)').matches;
if (reduced) return; // native scroll = the fallback
lenis = new Lenis({ lerp: 0.1, smoothWheel: true });
// ONE rAF driver for the whole app (Principle: shared clock)
lenis.on('scroll', () => {}); // ScrollTrigger.update hook lives in KineticText
gsap.ticker.add((t) => lenis?.raf(t * 1000));
gsap.ticker.lagSmoothing(0);
return () => { lenis?.destroy(); lenis = null; };
});
</script>
{@render children()}
```
### 5.2 WebGLCanvas — single renderer, context-provided scene graph
```svelte
<!-- $lib/webgl/WebGLCanvas.svelte -->
<script lang="ts">
import { onMount, type Snippet } from 'svelte';
import { provideWebGL } from './context';
let { children, dpr = 1.5 }: { children: Snippet; dpr?: number } = $props();
let canvas: HTMLCanvasElement;
let ready = $state(false);
onMount(async () => {
const THREE = await import('three'); // code-split: three never hits content pages
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, powerPreference: 'high-performance' });
renderer.setPixelRatio(Math.min(devicePixelRatio, dpr)); // cap DPR: free FPS on retina
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 0.1, 100);
const onResize = () => {
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
};
onResize();
addEventListener('resize', onResize);
renderer.setAnimationLoop(() => renderer.render(scene, camera));
provideWebGL({ renderer, scene, camera, canvas });
ready = true;
return () => {
removeEventListener('resize', onResize);
renderer.setAnimationLoop(null);
renderer.dispose();
};
});
</script>
<canvas bind:this={canvas} class="fixed inset-0 -z-10" aria-hidden="true"></canvas>
{#if ready}{@render children()}{/if}
```
### 5.3 KineticText — scroll-driven split typography
```svelte
<!-- $lib/components/KineticText.svelte -->
<script lang="ts">
import { onMount } from 'svelte';
let {
text,
as = 'h2',
scrub = true
}: { text: string; as?: keyof HTMLElementTagNameMap; scrub?: boolean } = $props();
let el: HTMLElement;
onMount(async () => {
if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const { animate } = await import('animejs');
const { splitText } = await import('animejs/text');
const { onScroll } = await import('animejs'); // Scroll Observer [^12^]
const words = splitText({ text, by: 'word', tag: 'span' });
el.append(...words.nodes as Node[]);
const anim = animate(words.nodes, {
opacity: { to: 1 },
translateY: { to: 0 },
delay: (el, i) => i * 40, // stagger → swap for gwoka pattern (Part 3)
ease: 'out(3)',
duration: 600,
autoplay: scrub
? onScroll({ target: el, enter: 'bottom-=10%', leave: 'top+=10%', sync: 0.5 })
: true
});
return () => anim.revert();
});
</script>
<svelte:element this={as} bind:this={el} aria-label={text} class="kinetic">{text}</svelte:element>
```
### 5.4 ParallaxLayer — multi-plane depth, CSS-first
```svelte
<!-- $lib/components/ParallaxLayer.svelte -->
<script lang="ts">
import { onMount, type Snippet } from 'svelte';
let { depth = 0.3, children }: { depth?: number; children: Snippet } = $props();
let el: HTMLElement;
onMount(async () => {
// Prefer native scroll-driven animations where supported (no JS per frame)
if (CSS.supports('animation-timeline: view()')) {
el.style.animationTimeline = 'view()';
el.style.animationName = 'parallax-y';
el.style.setProperty('--depth', String(depth));
return;
}
const gsap = (await import('gsap')).default;
const { ScrollTrigger } = await import('gsap/ScrollTrigger');
gsap.registerPlugin(ScrollTrigger);
const tween = gsap.to(el, {
yPercent: -depth * 100,
ease: 'none',
scrollTrigger: { trigger: el, start: 'top bottom', end: 'bottom top', scrub: true }
});
return () => { tween.scrollTrigger?.kill(); tween.kill(); };
});
</script>
<div bind:this={el} class="parallax-layer">{@render children()}</div>
<style>
@keyframes parallax-y { to { transform: translateY(calc(var(--depth, 0.3) * -30%)); } }
</style>
```
### 5.5 CursorTracker — physics cursor + velocity uniform (Igloo pattern)
```svelte
<!-- $lib/components/CursorTracker.svelte -->
<script lang="ts">
import { onMount } from 'svelte';
let dot: HTMLDivElement;
onMount(async () => {
if (matchMedia('(pointer: coarse)').matches) return; // no cursor on touch
const { createSpring } = await import('animejs');
const spring = createSpring({ stiffness: 120, damping: 14 });
let x = 0, y = 0, px = 0, py = 0;
const onMove = (e: PointerEvent) => { x = e.clientX; y = e.clientY; };
addEventListener('pointermove', onMove, { passive: true });
let raf = 0;
const tick = () => {
const vx = x - px, vy = y - py; // pointer velocity
const speed = Math.min(Math.hypot(vx, vy) / 40, 1);
dot.style.transform = `translate(${spring.solve(x, px)}px, ${spring.solve(y, py)}px) scale(${1 + speed * 0.6})`;
// → also write (x, y, speed) into your WebGL uniforms here: cursor-as-light-source (Unseen pattern)
px = x; py = y;
raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
return () => { removeEventListener('pointermove', onMove); cancelAnimationFrame(raf); };
});
</script>
<div bind:this={dot} class="cursor-dot" aria-hidden="true"></div>
```
### 5.6 PageTransition — GSAP route transitions (SvelteKit-safe)
```svelte
<!-- +layout.svelte (site group) -->
<script lang="ts">
import { onNavigate } from '$app/navigation';
import { page } from '$app/state';
onNavigate((nav) => {
// Prefer native View Transitions where available (progressive enhancement)
if (!document.startViewTransition) return;
return new Promise((resolve) => {
document.startViewTransition(async () => { resolve(); await nav.complete; });
});
});
</script>
{#key page.url.pathname}
<main class="page"><slot /></main>
{/key}
<style>
.page { view-transition-name: page; }
::view-transition-old(page) { animation: page-out 0.35s var(--ease-ka) both; }
::view-transition-new(page) { animation: page-in 0.45s var(--ease-ka) both; }
@keyframes page-out { to { opacity: 0; transform: translateY(-2%); } }
@keyframes page-in { from { opacity: 0; transform: translateY(3%); } }
</style>
```
*GSAP alternative for browsers without View Transitions:* animate out in `onNavigate`'s returned promise, animate in via `afterNavigate`. Keep durations in your CSS custom properties so both paths share the same motion tokens.
### 5.7 ScrollProgressBar — zétwal minimal progress
```svelte
<!-- $lib/components/ScrollProgressBar.svelte -->
<script lang="ts">
let progress = $state(0);
const onscroll = () => {
const max = document.documentElement.scrollHeight - innerHeight;
progress = max > 0 ? scrollY / max : 0;
};
</script>
<svelte:window {onscroll} />
<div class="progress" role="progressbar" aria-valuenow={Math.round(progress * 100)}
aria-valuemin="0" aria-valuemax="100" aria-label="Progression de lecture">
<span style:transform="scaleX({progress})"></span>
</div>
<style>
.progress { position: fixed; inset: 0 0 auto; height: 3px; z-index: 50; }
.progress span { display: block; height: 100%; transform-origin: left;
background: linear-gradient(90deg, var(--lagune-phospho), var(--or-circuit)); }
</style>
```
### 5.8 Bonus — FlipbookCanvas (Apple pattern, connection-aware)
```ts
// $lib/actions/flipbook.ts — Svelte action
export function flipbook(canvas: HTMLCanvasElement, opts: { frames: (q: 'full'|'lite') => string[]; total: number }) {
const conn = (navigator as any).connection;
const quality = conn?.effectiveType === '4g' && !conn?.saveData ? 'full' : 'lite';
const urls = opts.frames(quality);
if (conn?.effectiveType === 'slow-2g' || conn?.effectiveType === '2g' || conn?.saveData) {
// Apple doctrine: single fallback image, no sequence [^19^]
const img = new Image(); img.src = urls[0];
img.onload = () => canvas.getContext('2d')?.drawImage(img, 0, 0);
return {};
}
const imgs = urls.map((u) => { const i = new Image(); i.src = u; return i; });
const ctx = canvas.getContext('2d')!;
const draw = (p: number) => {
const f = Math.min(urls.length - 1, Math.floor(p * urls.length));
if (imgs[f]?.complete) ctx.drawImage(imgs[f], 0, 0, canvas.width, canvas.height);
};
const onscroll = () => {
const max = document.documentElement.scrollHeight - innerHeight;
requestAnimationFrame(() => draw(max > 0 ? scrollY / max : 0));
};
addEventListener('scroll', onscroll, { passive: true });
return { destroy: () => removeEventListener('scroll', onscroll) };
}
```
---
## 6. Svelte-Specific Translations (React/Next.js → Svelte idioms)
| React pattern (seen on these sites) | Svelte 5 idiom |
|---|---|
| `useRef` + `useEffect(() => {...}, [])` for GSAP init | `$effect(() => { /* init */; return () => cleanup })` — or better, a **Svelte action** `use:gsapThing` |
| `useContext(WebGLContext)` | `setContext`/`getContext` with a `Symbol` key (§2.2) |
| `useState` for animation params | `$state` rune; params read inside `$effect` re-run the tween — guard with `gsap.quickTo` or `quickSetter` for per-frame writes |
| Derived uniforms (`useMemo`) | `$derived(() => computeUniforms(pointer, scroll))` — recalculates only on dependency change |
| R3F `<Canvas>` + hooks | **Threlte** for declarative scenes; raw Three in `onMount` for shader-heavy work (Noomo avoided R3F for flexibility [^13^] — same logic applies) |
| `framer-motion` layout animations | Svelte built-in `flip` (from `svelte/animate`) + `crossfade` (from `svelte/transition`) — free, SSR-safe |
| Zustand/Redux for UI state | `$state` in a `.svelte.ts` module — a class with rune fields is your store |
**Runes vs stores for animation state:** stores still work and are fine for cross-app state; but **animation state should live in `$state`/`$derived` inside components or `.svelte.ts` modules** — runes give finer-grained reactivity (no subscriber fan-out per frame). Critical rule: **never write per-frame values (scrollY, pointer) into reactive state that drives DOM** — write them to refs/plain variables and flush via rAF, or you'll re-render at 60 Hz.
**GSAP + runes compatibility:** GSAP mutates the DOM directly — Svelte doesn't care as long as GSAP's targets aren't nodes Svelte also patches (avoid tweening attributes Svelte controls via bindings). Wrap GSAP targets in `{@html}`-free static markup or bind via `bind:this`, init in `$effect`, kill in the cleanup function. Anime.js v4's `createScope({ root })` + `scope.revert()` is the same lifecycle shape and tree-shakes better [^5^].
---
## 7. Accessibility & Fallbacks
1. **Screen readers in immersive sites:** canvas is `aria-hidden`; a semantic DOM mirror carries all content (Apple pattern [^19^]). If you render UI text in WebGL (Igloo), ship a visually-hidden parallel DOM (`sr-only` list of the same links/labels).
2. **Reduced motion:** one global gate — `matchMedia('(prefers-reduced-motion: reduce)')` checked in `SmoothScrollProvider` (no Lenis), `KineticText` (no split/scrub), `ParallaxLayer` (static), flipbook (poster frame). Implement it **once, at the provider level**, not per component.
3. **Graceful degradation ladder** (the Gwada ladder):
- `navigator.connection.saveData` or `effectiveType` ≤ 3g → static images, no autoplay video, no sequence preloading [^19^]
- `deviceMemory ≤ 4` or WebGL context creation fails → DOM-effects-only mode (Zentry proves DOM-only can still win awards [^6^])
- `<noscript>` / JS-disabled → SSR content is complete and readable (this is why SSR matters)
4. **Focus management:** route transitions must move focus to the new page's `<h1>`; scroll-jacking (Lenis) must never break keyboard scrolling — Lenis preserves native scroll, which is why it's the right engine choice.
5. **Bilingual a11y:** `lang` attributes per text node (`fr` / `gcf`) — screen readers switch voices; crucial for Kréyòl content.
---
## 8. Tooling Recommendations
### Essential packages (open-source first, per your doctrine)
```jsonc
{
"dependencies": {
"lenis": "^1.3", // smooth scroll — the award-site standard [^20^][^27^]
"gsap": "^3.13", // timelines, ScrollTrigger, ticker
"animejs": "^4.2", // modular springs/stagger/splitText [^5^]
"three": "^0.1xx", // WebGL — dynamic-import only
"@threlte/core": "^8", // optional: declarative Three in Svelte
"troika-three-text": "^0.5" // SDF text in WebGL (Igloo pattern, if needed)
},
"devDependencies": {
"@gltf-transform/cli": "^4",// meshopt/KTX2 pipeline (§4.2)
"vite-imagetools": "^7", // responsive AVIF/WebP at build
"sharp": "^0.33"
}
}
```
- **Vite plugins:** `vite-imagetools` (build-time responsive images), `vite-plugin-pwa` (offline mode = real resilience on flaky 4G, plus an installable PWA for AKILPA), basic `manualChunks` splitting `three` into its own async chunk.
- **HMR for WebGL:** Vite HMR reloads Svelte components but not your GL state — keep scene setup idempotent (init/dispose symmetric in `$effect`), store debug params in URL hash so a reload restores your camera/scrub position. Shader files as `?raw` imports hot-reload cleanly.
- **Hosting (≤50 €/mo, actually near 0 €):** **Cloudflare Pages + R2** — the exact Kriss.ai architecture [^28^]; free tier absorbs heavy media via edge CDN. Netlify/Vercel free tiers work too; avoid anything Node-server-based (Immersive Garden's Strapi [^27^] is out of budget — use a headless Git-based CMS or plain Markdown in-repo).
---
# PART 3 — CYBER-MAWONAJ ADAPTATION
How the 15-site DNA translates for a Caribbean, sovereign, 4G-first aesthetic. This is not "apply a tropical skin" — it's re-deriving each decision from the local context.
### Color system (CSS tokens, oklch for perceptual control)
```css
:root {
--lagune-phospho: oklch(72% 0.13 210); /* phosphorescent lagoon blue — primary accent */
--roche-volcan: oklch(18% 0.02 260); /* volcanic rock black — dominant ground */
--corail: oklch(70% 0.16 25); /* coral pink — secondary accent */
--or-circuit: oklch(80% 0.14 90); /* circuit gold — data/tech highlights */
--foret-neon: oklch(55% 0.15 155); /* deep forest green, used with neon edge-glow */
--terre-rouge-a: oklch(45% 0.12 40); /* terre rouge gradient stop A */
--terre-rouge-b: oklch(30% 0.09 55); /* terre rouge gradient stop B — use for section grounds */
}
```
Dark-mode-first (`--roche-volcan` ground) = OLED battery saving + the bioluminescence aesthetic for free. The **arc-en-ciel / rainbow-serpent** motif belongs in *gradients and particle hues* (velocity-shifted color, Igloo pattern [^1^]) — never as static rainbow fills.
### Cultural signifiers → concrete UI patterns
| Signifier | Pattern (source site) | Implementation |
|---|---|---|
| **Lambi shell** | UI motif: shell-spiral as scroll progress, loader, section dividers | SVG spiral `stroke-dashoffset` scrub (createDrawable, animejs/svg [^5^]); KTX2 shell texture for WebGL hero |
| **Zétwal (stars)** | Navigation: constellation nav where sections are stars | DOM + SVG lines; hover = star brightens + label reveals; light-years cheaper than a WebGL starfield |
| **Dollhouse créole** | Kriss.ai spatial IA [^28^] | AKILPA sections as case à ka / marché / karbay / plage rooms — isometric DOM illustration, no WebGL needed |
| **Terre rouge** | Section backgrounds gradient | Pure CSS, zero cost |
| **Mawon/maroon resistance** | Visual language: glitch-free, hand-made texture; KPR's community-shaped tableaux [^45^] | UGC/content layers: community-submitted photos as parallax planes; "Backstage" section documenting the process (Immersive Garden pattern [^27^]) = transparency as sovereignty |
### Typography (FR/KRÉ bilingual, low-res safe)
- **Weights: ≥ 400 body, ≥ 600 display** — thin weights die on low-DPI screens (your constraint is correct).
- Suggested open fonts: **Space Grotesk** (display, tech-caribbean tension) + **Inter** or **Atkinson Hyperlegible** (body, legibility-first, open license). Atkinson is the sovereignty-aligned choice: designed for low-vision readers.
- Kinetic type via `splitText` on **words, not characters**, for Kréyòl — character-splitting can break apostrophe-heavy words (`mwen`, `tèt'w`) visually and for screen readers. Keep `aria-label` with the intact string (component §5.3 does this).
- Language switch as animation event (Nudot pattern): mask-swap lines in place.
### Motion design — gwoka cadences as easing/timing tokens
Award sites ship custom ease curves; yours can carry rhythm. Practical translation:
```css
:root {
/* ka/belair-inspired: strong downbeat, syncopated release */
--ease-ka: cubic-bezier(0.22, 1, 0.36, 1); /* downbeat: fast attack, long settle */
--ease-syncope: cubic-bezier(0.65, 0, 0.35, 1); /* off-beat: symmetric, urgent */
--dur-tanbou: 120ms; /* sixteenth @ ~125 BPM — micro-interactions */
--dur-mesure: 480ms; /* one bar — entrances */
--dur-phrase: 960ms; /* two bars — page transitions */
}
```
Stagger patterns: instead of linear `i * 40ms`, use **syncopated stagger** `[0, 120, 240, 360, 480, 600, 720, 840]` → remap to `[0, 120, 300, 360, 600, 660, 900, 960]` (3+3+2 feel). Subtle, but rhythm is identity.
### The Gwada connectivity doctrine (non-negotiables)
1. **Connection-aware everything** — flipbook component §5.8 is the reference implementation [^19^].
2. **DOM-first effects** — Zentry is your patron saint here [^6^].
3. **PWA with offline shell** — an association site that works in a dead zone is a political statement (souveraineté numérique).
4. **Test hardware:** a ~150 € Android on real 4G in Gwada, not Lighthouse throttling.
5. **Edge hosting:** Cloudflare Pages/R2 [^28^] — points of presence in the Americas, near-zero cost, aligns with the 50 €/mo ceiling (realistically: < 5 €/mo).
---
# PART 4 — COMPARISON MATRIX
| # | Site | Visual | Motion | Tech | UX | Stealable pattern |
|---|---|---|---|---|---|---|
| 1 | unseen.co | Volumetric light, dark bloom | Inertial, particle-driven | Three.js [^39^] | Scroll-journey portfolio | Cursor-as-light uniform |
| 2 | animejs.com | Lab-instrument dark, kinetic type | Springs, staggers, scroll-observer | Three hero + Anime.js v4 modules [^5^][^14^] | Interactive docs | `createScope` per component |
| 3 | immersive-g.com | Bas-relief minimalism | Ceremonial glide | Nuxt, Three, GSAP, Lenis, Strapi, KTX pipeline [^27^] | One-click access + Backstage | Backstage process section |
| 4 | davidwhyte.com/experience | Watercolor organic | Fluid sim, baked-noise reveals | Nuxt, Three, GSAP, Lenis, WP [^20^] | Slow contemplative tempo | Bake generative noise offline |
| 5 | kriss.ai/aftercare | 70s warm dollhouse | Room-to-room camera | WebGL/Blender, **Cloudflare Pages+R2** [^28^] | Spatial metaphor IA | Dollhouse IA (créole rooms) |
| 6 | zentry.com | Bold saturated portals | Motion-system-first | **Nuxt, pure DOM — no WebGL** [^6^] | Portal reveals + sound | DOM-only 3D portal masks ⭐ |
| 7 | igloo.inc | Procedural ice, chromatic frost | Velocity particles, SDF glitch | **Three + Svelte + GSAP** [^1^] | 3-section scroll journey | Velocity-reactive particles |
| 8 | kprverse.com | Concept-art tableaux | Click-and-hold layers | Resn (Nuxt-class) [^45^] | Lore gating, terminal loader | Click-hold reveal layer |
| 9 | hape.io | Fashion editorial | Scroll-scrubbed walk + music sync | Custom OGL micro-framework [^37^] | Single-idea focus | Scroll-scrubbed animation clip |
| 10 | labs.noomoagency.com | Glass jellyfish dream | Baked camera playback | Three + GSAP, no R3F, Blender-baked [^13^] | Public R&D hub | Two-Empty baked camera rig |
| 11 | noomoagency.com | Disciplined glassmorphism | Per-element hover craft | Nuxt 3 full-stack, Three, GSAP [^9^] | Classic IA, flawless transitions | Budgeted hover craft (5 elements) |
| 12 | nudot.com.tw | Bilingual kinetic type | Character reveals, hover previews | DOM/GSAP-class *(inference)* | Bilingual as feature | Language-switch as animation |
| 13 | 375.studio | Two-color discipline | Systematic micro-motion | DOM-first *(inference)* | Every state designed | Designed 404/empty states |
| 14 | ten.375.studio | Interactive comic | Panel scrubbing, mouse trail | DOM/SVG *(inference)* | Chaptered narrative | Chaptered scroll-comic ⭐ |
| 15 | apple.com/airpods-pro | Photographic black | Scroll flipbook, master timeline | Canvas 2D image sequence [^19^][^18^] | Desire before specs | Connection-aware flipbook ⭐ |
⭐ = highest ROI for Cyber-mawonaj constraints (4G, low-end, sovereignty).
---
## Sources
[^1^]: https://www.webgpu.com/showcase/igloo-inc-procedural-crystals/ — Igloo Inc: Crystal Growth Algorithms, Shader-Driven UI, and Volume Data (Feb 2026)
[^3^]: https://www.awwwards.com/sites/igloo-inc — Igloo Inc, Awwwards SOTD Jul 23 2024
[^4^]: https://www.awwwards.com/sites/ten-years-away — Ten Years Away, Awwwards SOTD Jun 26 2026
[^5^]: https://github.com/juliangarnier/anime/issues/1105 — Anime.js v4 LLM guiding file (API modules, createScope, splitText)
[^6^]: https://www.awwwards.com/zentry-case-study.html — Zentry case study (Resn): Nuxt/Vue/Vite, DOM-only 3D portal masks, no WebGL
[^7^]: https://www.awwwards.com/igloo-inc-case-study.html — Igloo Inc case study (Abeto × Bureaux)
[^8^]: https://www.webgpu.com/showcase/noomo-3d-storytelling-threejs/ — Noomo 3D storytelling site write-up
[^9^]: https://medium.com/@noomo-agency/noomo-agency-website-of-the-year-winner-on-awwwards-aad757327994 — Noomo: Nuxt 3, Three.js, GSAP
[^10^]: https://www.awwwards.com/sites/studio375 — Studio375 HM (2023): elements incl. animated 404
[^11^]: https://www.awwwards.com/sites/noomo-agency — Noomo Agency SOTD Sep 21 2023, elements list
[^12^]: https://animejs.com/ — Anime.js feature docs (Scroll Observer, stagger, SVG toolset)
[^13^]: https://www.awwwards.com/noomo-labs-a-hub-for-cutting-edge-immersive-experiences.html — Noomo Labs behind the scenes: Three+GSAP, no R3F, Blender-baked, two-Empty camera
[^14^]: https://www.reddit.com/r/webdev/comments/1jqmqkn/ — r/webdev on animejs.com v4 site (Three.js hero)
[^15^]: https://www.awwwards.com/Studio375/ — Studio375 profile & awards
[^16^]: https://noomoagency.com/ — Noomo Agency honors list (WOTY, Labs awards)
[^17^]: https://www.awwwards.com/sites/david-whyte-experience — David Whyte Experience SOTD Dec 31 2024
[^18^]: https://gsap.com/community/forums/topic/25188-airpods-image-sequence-animation-using-scrolltrigger/ — master-timeline percentage technique
[^19^]: https://css-tricks.com/lets-make-one-of-those-fancy-scrolling-animations-used-on-apple-product-pages/ — Apple flipbook technique + slow-connection fallback
[^20^]: https://www.awwwards.com/case-study-david-whyte-experience-by-immersive-garden.html — fluid-sim atlas, baked noise, full stack (Nuxt/Three/GSAP/Lenis/WP/Vercel)
[^24^]: https://www.awwwards.com/immersivegarden/ — Immersive Garden profile (Studio of the Year ×3)
[^27^]: https://www.awwwards.com/case-study-immersive-gardens-new-website.html — KTX compression, channel packing, gltf-transform; Nuxt/Three/GSAP/Lenis/Strapi/Vercel
[^28^]: https://www.awwwards.com/case-study-ai-with-a-human-touch-the-new-face-of-kriss-ai.html — Kriss.ai dollhouse concept; Cloudflare Pages + R2
[^35^]: https://www.awwwards.com/sites/kpr — KPR SOTD Dec 26 2022
[^36^]: https://www.awwwards.com/sites/hape — HAPE SOTD Apr 27 2022
[^37^]: https://www.deptagency.com/case/creating-a-3d-nft-walking-hape/ — HAPE: custom OGL micro-framework, PBR, skinning; 4-week build
[^38^]: https://www.awwwards.com/directory/agency-studio/ — Unseen Studio directory stats (35 SOTD)
[^39^]: https://x.com/uns__nstudio — Unseen Studio: rebrand from GC, volumetric studies, particle sims, three.js tags
[^40^]: https://dogstudio.co/cases/hape-prime/ — HAPE walk concept, scroll camera, soundtrack sync
[^41^]: https://kprverse.com/ — KPR live site: terminal loader strings, device gating messages
[^45^]: https://www.awwwards.com/kpr-by-resn-wins-sotm.html — KPR by Resn SOTM: click-and-hold layers, tableaux, 3D+2D mix