@struq/skill 0.0.0-stage → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +141 -2
  3. package/cli/struq.mjs +132 -0
  4. package/package.json +41 -4
  5. package/skills/struq/SKILL.md +237 -0
  6. package/skills/struq/cinematic/README.md +409 -0
  7. package/skills/struq/cinematic/checklists/01-preproduction.md +15 -0
  8. package/skills/struq/cinematic/checklists/02-implementation.md +17 -0
  9. package/skills/struq/cinematic/checklists/03-polish.md +17 -0
  10. package/skills/struq/cinematic/examples/code/nextjs/README.md +13 -0
  11. package/skills/struq/cinematic/examples/code/nextjs/app/cinematic-experience.tsx +13 -0
  12. package/skills/struq/cinematic/examples/code/nextjs/app/components/LenisRoot.tsx +42 -0
  13. package/skills/struq/cinematic/examples/code/nextjs/app/page.tsx +6 -0
  14. package/skills/struq/cinematic/examples/code/shared/CameraRig.tsx +65 -0
  15. package/skills/struq/cinematic/examples/code/shared/ExperienceCanvas.tsx +20 -0
  16. package/skills/struq/cinematic/examples/code/shared/ExperienceDirector.tsx +57 -0
  17. package/skills/struq/cinematic/examples/code/shared/ExperienceOverlay.tsx +37 -0
  18. package/skills/struq/cinematic/examples/code/shared/ExperiencePage.tsx +52 -0
  19. package/skills/struq/cinematic/examples/code/shared/GsapFrameLoop.tsx +32 -0
  20. package/skills/struq/cinematic/examples/code/shared/InteriorRoot.tsx +30 -0
  21. package/skills/struq/cinematic/examples/code/shared/README.md +20 -0
  22. package/skills/struq/cinematic/examples/code/shared/SubjectRig.tsx +58 -0
  23. package/skills/struq/cinematic/examples/code/shared/build-experience-timeline.ts +237 -0
  24. package/skills/struq/cinematic/examples/code/shared/direction.ts +52 -0
  25. package/skills/struq/cinematic/examples/code/shared/gsap-client.ts +32 -0
  26. package/skills/struq/cinematic/examples/code/shared/math.ts +12 -0
  27. package/skills/struq/cinematic/examples/code/shared/registry.ts +58 -0
  28. package/skills/struq/cinematic/examples/code/shared/runtime.ts +52 -0
  29. package/skills/struq/cinematic/examples/code/shared/styles.css +127 -0
  30. package/skills/struq/cinematic/examples/code/shared/useExperienceInput.ts +42 -0
  31. package/skills/struq/cinematic/examples/code/vite/README.md +14 -0
  32. package/skills/struq/cinematic/examples/code/vite/src/App.tsx +11 -0
  33. package/skills/struq/cinematic/examples/code/vite/src/LenisRoot.tsx +37 -0
  34. package/skills/struq/cinematic/examples/code/vite/src/main.tsx +9 -0
  35. package/skills/struq/cinematic/examples/concise/01-chapter-spec-example.md +42 -0
  36. package/skills/struq/cinematic/examples/concise/02-handoff-example.md +25 -0
  37. package/skills/struq/cinematic/examples/concise/03-audit-finding-example.md +13 -0
  38. package/skills/struq/cinematic/examples/verbose/01-full-experience-direction.md +687 -0
  39. package/skills/struq/cinematic/examples/verbose/02-full-audit-example.md +362 -0
  40. package/skills/struq/cinematic/references/01-core-mental-model.md +221 -0
  41. package/skills/struq/cinematic/references/02-vocabulary.md +384 -0
  42. package/skills/struq/cinematic/references/03-experience-architecture.md +302 -0
  43. package/skills/struq/cinematic/references/04-timeline-choreography.md +264 -0
  44. package/skills/struq/cinematic/references/05-handoff-contracts.md +241 -0
  45. package/skills/struq/cinematic/references/06-three-rig-architecture.md +286 -0
  46. package/skills/struq/cinematic/references/07-stack-integration.md +228 -0
  47. package/skills/struq/cinematic/references/08-gsap-plugin-patterns.md +218 -0
  48. package/skills/struq/cinematic/references/09-tuning-harness.md +202 -0
  49. package/skills/struq/cinematic/references/10-quality-systems.md +206 -0
  50. package/skills/struq/cinematic/references/11-agent-workflows.md +246 -0
  51. package/skills/struq/cinematic/references/12-audit-rubric.md +176 -0
  52. package/skills/struq/cinematic/references/13-official-docs.md +53 -0
  53. package/skills/struq/cinematic/templates/01-experience-brief.md +105 -0
  54. package/skills/struq/cinematic/templates/02-experience-contract.md +80 -0
  55. package/skills/struq/cinematic/templates/03-chapter-spec.md +103 -0
  56. package/skills/struq/cinematic/templates/04-handoff-contract.md +102 -0
  57. package/skills/struq/cinematic/templates/05-ownership-matrix.md +29 -0
  58. package/skills/struq/cinematic/templates/06-shot-presets.md +59 -0
  59. package/skills/struq/cinematic/templates/07-implementation-slice.md +66 -0
  60. package/skills/struq/cinematic/templates/08-tuning-register.md +36 -0
  61. package/skills/struq/cinematic/templates/09-acceptance-tests.md +57 -0
  62. package/skills/struq/references/anti-patterns.md +265 -0
  63. package/skills/struq/references/baseline-prompt.md +118 -0
  64. package/skills/struq/references/brand.md +143 -0
  65. package/skills/struq/references/brief-router.md +219 -0
  66. package/skills/struq/references/composition.md +191 -0
  67. package/skills/struq/references/depth-parallax.md +235 -0
  68. package/skills/struq/references/direction.md +188 -0
  69. package/skills/struq/references/feel-layer.md +107 -0
  70. package/skills/struq/references/gsap-api.md +293 -0
  71. package/skills/struq/references/iconography.md +255 -0
  72. package/skills/struq/references/imagery.md +144 -0
  73. package/skills/struq/references/loaders.md +287 -0
  74. package/skills/struq/references/micro-interactions.md +270 -0
  75. package/skills/struq/references/mobile-composition.md +70 -0
  76. package/skills/struq/references/nav-header.md +296 -0
  77. package/skills/struq/references/page-architecture.md +134 -0
  78. package/skills/struq/references/page-entrances.md +198 -0
  79. package/skills/struq/references/performance.md +235 -0
  80. package/skills/struq/references/pinning-scrub.md +341 -0
  81. package/skills/struq/references/pitfalls.md +290 -0
  82. package/skills/struq/references/route-transitions.md +281 -0
  83. package/skills/struq/references/scaling.md +151 -0
  84. package/skills/struq/references/scroll-reveals.md +280 -0
  85. package/skills/struq/references/seo.md +219 -0
  86. package/skills/struq/references/setup.md +397 -0
  87. package/skills/struq/references/showcase.md +87 -0
  88. package/skills/struq/references/stack-targets.md +78 -0
  89. package/skills/struq/references/struq-imagery.md +107 -0
  90. package/skills/struq/references/svg-motion.md +390 -0
  91. package/skills/struq/references/transitions.md +160 -0
  92. package/skills/struq/references/triage.md +71 -0
  93. package/skills/struq/references/typography.md +720 -0
  94. package/skills/struq/references/vibe-presets.md +287 -0
  95. package/skills/struq/references/webgl-r3f.md +274 -0
  96. package/skills/struq/references/webgl-scenes.md +593 -0
@@ -0,0 +1,362 @@
1
+ # Full Audit Example: Generic but Technically Impressive Portfolio
2
+
3
+ ## Evidence supplied
4
+
5
+ The current portfolio contains:
6
+
7
+ - six `100vh` sections,
8
+ - a fixed R3F canvas,
9
+ - one model per section toggled by active index,
10
+ - Lenis and ScrollTrigger,
11
+ - separate GSAP timelines inside each section,
12
+ - Motion hover effects on cards,
13
+ - pointer camera parallax inside `useFrame`,
14
+ - post-processing bloom and chromatic aberration,
15
+ - section titles revealed with SplitText,
16
+ - a recording showing fast-scroll jumps and repeated fade transitions.
17
+
18
+ ## Executive diagnosis
19
+
20
+ The implementation has strong individual effects but no authoritative experience state or handoff system. Each section behaves as an independent animation scene, while the fixed canvas only creates the appearance of persistence. Camera, model visibility, and environment state reset at each boundary. Multiple systems write to camera and DOM transforms, so final poses vary by input speed. The result feels like six demos placed in sequence rather than one directed experience.
21
+
22
+ ## Scorecard
23
+
24
+ | Category | Score / 5 | Evidence | Priority |
25
+ |---|---:|---|---|
26
+ | Experience thesis | 2 | “Experimental portfolio” is a style, not a transformation. | High |
27
+ | Persistent continuity | 2 | Canvas persists, but scene state resets per section. | Critical |
28
+ | Chapter purpose | 2 | Sections correspond to content categories only. | High |
29
+ | Handoff quality | 1 | Opacity crossfade and model visibility toggle. | Critical |
30
+ | State continuity | 1 | Camera and model transforms jump to defaults. | Critical |
31
+ | Property ownership | 1 | GSAP and `useFrame` both write camera transform. | Critical |
32
+ | Timeline hierarchy | 2 | Local timelines exist but no route or handoff composition. | High |
33
+ | Semantic cues | 1 | Anonymous numeric offsets. | Medium |
34
+ | Rhythm and pacing | 2 | Continuous motion with no reading holds. | High |
35
+ | Camera direction | 2 | Repeated orbit and push-in, no named shots. | Critical |
36
+ | Subject choreography | 3 | Good model animation inside sections. | Medium |
37
+ | Typography | 2 | Same SplitText reveal in every section. | Medium |
38
+ | SVG integration | 0 | None. | Optional |
39
+ | Input arbitration | 1 | Pointer fights scroll camera. | Critical |
40
+ | Interaction envelopes | 1 | Hover remains active during transitions. | High |
41
+ | Reverse behavior | 1 | Visibility callbacks leave stale models. | Critical |
42
+ | Responsive choreography | 1 | Desktop values multiplied by 0.7. | High |
43
+ | Reduced motion | 0 | Not implemented. | High |
44
+ | Performance | 2 | All models stay active; post effects fixed at full quality. | High |
45
+ | Lifecycle | 2 | Some cleanup exists; duplicate triggers after route return. | Critical |
46
+ | Tuning readiness | 1 | Values spread across components. | High |
47
+ | Validation | 1 | Desktop forward scroll only. | High |
48
+
49
+ **Total:** 30 / 110
50
+
51
+ The page is visually capable but structurally weak. Decorative polish should pause until continuity, ownership, and lifecycle are corrected.
52
+
53
+ ## Current continuity map
54
+
55
+ | Boundary | Persistent element | Current transition | Camera continuity | Ownership | Result |
56
+ |---|---|---|---|---|---|
57
+ | Hero → About | canvas only | hero fades; portrait model toggles on | camera reset | both sections briefly write | visible snap |
58
+ | About → Work | canvas only | background crossfade | orbit restarts | active index callback | generic section change |
59
+ | Work → Project 1 | card thumbnail | DOM card scales; 3D model appears separately | no shared pose | Motion and GSAP overlap | broken shared-element illusion |
60
+ | Project 1 → Project 2 | none | opacity crossfade | camera returns to origin | callbacks | full reset |
61
+ | Projects → Contact | grain | all 3D fades out | camera unused | unclear | experience collapses into normal footer |
62
+
63
+ ## Property-conflict map
64
+
65
+ ### Camera position
66
+
67
+ **Writers:**
68
+
69
+ - hero GSAP timeline,
70
+ - about GSAP timeline,
71
+ - project GSAP timeline,
72
+ - pointer `useFrame` lerp,
73
+ - route transition.
74
+
75
+ **Symptom:** final framing varies by scroll speed and pointer position.
76
+
77
+ **Correction:** introduce route, narrative, reaction, and shake rigs. Chapters and handoffs own only the narrative rig. Pointer owns reaction. Route transition owns a parent route rig.
78
+
79
+ ### Model visibility
80
+
81
+ **Writers:**
82
+
83
+ - active-section React state,
84
+ - GSAP `autoAlpha` proxy,
85
+ - conditional mounting.
86
+
87
+ **Symptom:** reverse scroll leaves the wrong model visible; mounting causes frame drops.
88
+
89
+ **Correction:** retain adjacent bridge assets, derive visibility from local progress, and transfer scene-group ownership through handoff timelines. Unmount only outside reversible ranges.
90
+
91
+ ### DOM project card transform
92
+
93
+ **Writers:**
94
+
95
+ - Motion hover scale,
96
+ - GSAP project-open timeline,
97
+ - CSS transition.
98
+
99
+ **Symptom:** card jumps at click and exits from an unpredictable scale.
100
+
101
+ **Correction:** assign Motion to an inner hover wrapper and GSAP/Flip to an outer transition wrapper. Remove CSS transform transition from the outer layer.
102
+
103
+ ### Chromatic aberration
104
+
105
+ **Writers:**
106
+
107
+ - raw scroll velocity,
108
+ - project transition timeline.
109
+
110
+ **Symptom:** effect spikes during fast scroll and remains elevated after route interruption.
111
+
112
+ **Correction:** separate `velocityAberration` and `transitionAberration` channels, clamp and damp them, and resolve the final value centrally.
113
+
114
+ ## Architecture findings
115
+
116
+ ### Finding 1: Section index is not an experience state model
117
+
118
+ The current `activeSection` integer controls model visibility and copy, but it cannot represent overlaps, handoff progress, interaction gating, or transition ownership.
119
+
120
+ **Correction:** introduce chapter and handoff state:
121
+
122
+ ```ts
123
+ {
124
+ activeChapter,
125
+ chapterProgress,
126
+ activeHandoff,
127
+ handoffProgress,
128
+ interactionMode,
129
+ cameraOwner,
130
+ viewportClass,
131
+ reducedMotion,
132
+ performanceTier
133
+ }
134
+ ```
135
+
136
+ Keep high-frequency values outside React render state.
137
+
138
+ ### Finding 2: Fixed canvas without persistent state is superficial continuity
139
+
140
+ The canvas remains mounted, but each section restores default camera, background, light, and model transforms.
141
+
142
+ **Correction:** identify one persistent visual motif. For this portfolio, use a “lens plane” that changes role:
143
+
144
+ - hero: abstract refractive plane,
145
+ - about: portrait distortion surface,
146
+ - work: project index portal,
147
+ - project: view into project world,
148
+ - contact: plane resolves into a clear glass interface.
149
+
150
+ The lens plane becomes the bridge. Camera and lighting evolve around it.
151
+
152
+ ### Finding 3: Chapter construction follows content categories
153
+
154
+ “About,” “Work,” and “Contact” are information labels, not visual actions.
155
+
156
+ **Correction:** redefine chapters as transformations:
157
+
158
+ | Current | Directed chapter | Visual action |
159
+ |---|---|---|
160
+ | Hero | `arrival` | lens plane condenses from noise into a stable portal |
161
+ | About | `identity` | portrait and biography emerge through the lens depth field |
162
+ | Work index | `selection` | lens multiplies into project apertures along a path |
163
+ | Project detail | `immersion` | selected aperture expands and becomes the project world |
164
+ | Contact | `return` | project world collapses into one transparent communication plane |
165
+
166
+ Semantic headings can remain About, Work, and Contact in the DOM.
167
+
168
+ ## Choreography findings
169
+
170
+ ### Repeated reveal language
171
+
172
+ Every heading reveals by characters from `yPercent: 100` with the same stagger and ease.
173
+
174
+ **Consequence:** chapters have no differentiated character.
175
+
176
+ **Correction:** define a small branded vocabulary:
177
+
178
+ - arrival titles: line masks with slow contrast emergence,
179
+ - identity copy: words refracted across the lens axis,
180
+ - work labels: path-ordered reveals along project apertures,
181
+ - project title: screen-space matched expansion from selected card,
182
+ - contact: restrained opacity and line drawing with no large travel.
183
+
184
+ ### No holds
185
+
186
+ The camera, model, type, and background move throughout each scroll range.
187
+
188
+ **Correction:** add stable intervals:
189
+
190
+ - arrival hero hold after lens resolves,
191
+ - biography reading hold with camera fixed and pointer response enabled,
192
+ - work selection hold where pointer exploration is primary,
193
+ - project hero hold before deeper narrative progression,
194
+ - contact final state with no forced movement.
195
+
196
+ ### Equal emphasis
197
+
198
+ Bloom, scale, camera travel, and SplitText all peak together at each boundary.
199
+
200
+ **Correction:** assign one apex per transition. For hero-to-identity, make the lens refraction and portrait emergence primary; camera and typography support it at lower intensity.
201
+
202
+ ## Camera findings
203
+
204
+ ### Current pattern
205
+
206
+ Each section uses an orbit from left to right plus a forward move. Camera target remains at world origin.
207
+
208
+ ### Problems
209
+
210
+ - repeated movement language,
211
+ - no target leading,
212
+ - no composition safe areas,
213
+ - pointer drift changes final shot,
214
+ - mobile crops subjects,
215
+ - no visual reason for the orbit.
216
+
217
+ ### New shot plan
218
+
219
+ ```text
220
+ arrivalWide
221
+ identityPortrait
222
+ selectionTopological
223
+ projectApertureMacro
224
+ projectWorldHero
225
+ contactResolve
226
+ ```
227
+
228
+ Each shot receives desktop and mobile variants. Target anchors follow the lens, portrait eye line, selected project aperture, and contact plane rather than remaining at origin.
229
+
230
+ ## Handoff redesign
231
+
232
+ ### Hero → Identity
233
+
234
+ **Bridge:** refractive lens plane.
235
+
236
+ The plane moves from abstract central composition to a right-side portrait lens. The camera trucks slightly left while the target follows the lens. Portrait texture appears inside the plane before abstract noise fades. The hero title compresses into a small vertical identity marker.
237
+
238
+ ### Identity → Selection
239
+
240
+ **Bridge:** lens plane splits into three project apertures.
241
+
242
+ Use a vector line as a spatial guide. Biography copy exits along that path. Apertures appear sequentially, but the original lens remains the first aperture, preserving continuity.
243
+
244
+ ### Selection → Project detail
245
+
246
+ **Bridge:** selected project aperture and DOM card.
247
+
248
+ Use Flip on an outer DOM project-card wrapper to preserve screen-space bounds while the WebGL aperture matches those bounds. Expand both toward the viewport under one GSAP transition timeline. Motion retains hover on an inner wrapper and is disabled after click intent.
249
+
250
+ ### Project detail → Contact
251
+
252
+ **Bridge:** project color field contracts into a clear glass plane.
253
+
254
+ The camera pulls out rather than resetting. Project typography reduces into contact metadata. The final state releases the persistent stage into normal document flow.
255
+
256
+ ## Responsive findings
257
+
258
+ The current mobile version multiplies camera positions and model scale by constants. This fails because composition is not linear.
259
+
260
+ ### Corrections
261
+
262
+ - author mobile shots,
263
+ - use centered lens compositions,
264
+ - reduce lateral camera movement,
265
+ - move biography into normal flow during the reading hold,
266
+ - replace hover exploration with tap selection,
267
+ - use a shorter project aperture transition,
268
+ - increase bridge coverage based on the short viewport edge,
269
+ - reduce simultaneous model count and post-processing.
270
+
271
+ ## Reduced-motion plan
272
+
273
+ - preserve lens-plane continuity,
274
+ - move between matched shots with short masked transitions,
275
+ - remove orbit, large parallax, and chromatic velocity effect,
276
+ - use opacity and small scale changes for type,
277
+ - keep project selection keyboard accessible,
278
+ - make normal document flow available without scroll trapping.
279
+
280
+ ## Performance findings
281
+
282
+ 1. All six models render continuously, including invisible models.
283
+ 2. Three full-resolution render targets remain allocated.
284
+ 3. DPR is uncapped.
285
+ 4. Chromatic aberration and bloom run at all times.
286
+ 5. pointer movement sets React state.
287
+ 6. models mount at section callbacks, causing decode and shader work during transitions.
288
+
289
+ ### Correction sequence
290
+
291
+ - preload only current and next bridge assets,
292
+ - retain adjacent models through reversible ranges,
293
+ - disable rendering for inactive groups,
294
+ - cap DPR by tier,
295
+ - reduce render-target resolution,
296
+ - move pointer values to refs,
297
+ - centralize effects channels,
298
+ - profile route return for leaks.
299
+
300
+ ## Lifecycle findings
301
+
302
+ `ScrollTrigger.getAll().forEach(kill)` is called globally on one route, affecting unrelated site triggers. A Lenis ticker callback is added on every mount but removed with a different function identity.
303
+
304
+ ### Correction
305
+
306
+ - scope timeline and trigger instances to the route,
307
+ - retain callback references for removal,
308
+ - use `useGSAP` or a scoped context,
309
+ - dispose route render targets,
310
+ - restore document overflow and cursor state,
311
+ - test browser back/forward.
312
+
313
+ ## Remediation sequence
314
+
315
+ ### Stage 1: State and rig foundation
316
+
317
+ - introduce experience runtime,
318
+ - create camera rig hierarchy,
319
+ - centralize direction data,
320
+ - remove direct pointer writes to camera narrative transforms,
321
+ - fix ticker cleanup.
322
+
323
+ **Validation:** current hero behaves identically after repeated mount/unmount and pointer movement does not alter narrative end pose.
324
+
325
+ ### Stage 2: First directed vertical slice
326
+
327
+ Rebuild hero-to-identity using the lens bridge, named shots, one handoff timeline, responsive variants, and reduced motion.
328
+
329
+ **Validation:** forward and reverse boundary contains no reset or opacity-only swap.
330
+
331
+ ### Stage 3: Work selection architecture
332
+
333
+ Create persistent aperture system, interaction envelope, and screen-space alignment between DOM cards and WebGL apertures.
334
+
335
+ ### Stage 4: Project transition
336
+
337
+ Separate Motion hover and GSAP/Flip transition wrappers. Implement route cover and destination readiness.
338
+
339
+ ### Stage 5: Remaining chapters and quality
340
+
341
+ Migrate contact resolution, performance tiers, accessibility, recordings, and full rubric audit.
342
+
343
+ ## Before-and-after behavior specification
344
+
345
+ ### Before
346
+
347
+ The hero model fades out. The camera returns to origin. The about portrait fades in. The same character stagger reveals the heading. Pointer motion continues throughout.
348
+
349
+ ### After
350
+
351
+ The hero lens plane moves toward the portrait composition while remaining visible. The camera target follows it before the camera trucks left. The portrait appears inside the lens while the abstract shader still surrounds it. The hero title compresses into a vertical identity marker. Pointer influence reduces during the transfer and returns during the biography hold. No camera or environment reset occurs.
352
+
353
+ ## Validation tests
354
+
355
+ - repeat every handoff forward and backward ten times,
356
+ - compare final shot transforms after slow and fast scroll,
357
+ - test pointer at all viewport corners during camera landings,
358
+ - resize inside each handoff,
359
+ - test route leave during transition and browser back,
360
+ - test mobile portrait and short-height laptop,
361
+ - test reduced motion and keyboard-only navigation,
362
+ - inspect trigger, ticker, listener, texture, and render-target counts after five route cycles.
@@ -0,0 +1,221 @@
1
+ # Core Mental Model
2
+
3
+ ## The experience is a directed state machine
4
+
5
+ A normal website is organized primarily by document structure. A cinematic WebGL experience is organized by **experience state**.
6
+
7
+ The page still has semantic HTML, routes, accessibility, and responsive layout, but the visible composition at any moment is resolved from several coordinated inputs:
8
+
9
+ ```text
10
+ final visual state =
11
+ narrative state
12
+ + ambient state
13
+ + reactive state
14
+ + discrete transition state
15
+ + responsive constraints
16
+ + performance constraints
17
+ ```
18
+
19
+ The core problem is not “how do I animate this element?” It is:
20
+
21
+ > Who owns this property, during which interval, while moving from which state to which state, and how does that state become the valid starting point for what follows?
22
+
23
+ That question is the foundation of professional choreography.
24
+
25
+ ## Persistent stage versus section stack
26
+
27
+ A section stack works like this:
28
+
29
+ ```text
30
+ hero mounts → hero animates → hero disappears
31
+ feature mounts → feature animates → feature disappears
32
+ project mounts → project animates → project disappears
33
+ ```
34
+
35
+ A persistent stage works like this:
36
+
37
+ ```text
38
+ world state A
39
+ → world state A/B overlap
40
+ → world state B
41
+ → world state B/C overlap
42
+ → world state C
43
+ ```
44
+
45
+ The canvas, camera hierarchy, lighting rig, central subject, grain, cursor, and some typography may remain alive throughout. Chapters transform their roles and values rather than replacing the entire world.
46
+
47
+ Use multiple scenes only when there is a strong reason, such as materially different render settings, asset isolation, route-level ownership, or render-target compositing. Even then, the visible transition should preserve continuity through a bridge.
48
+
49
+ ## Chapters are controlled state intervals
50
+
51
+ A chapter is not defined by a `<section>` tag. It is defined by:
52
+
53
+ - a narrative purpose,
54
+ - an input interval,
55
+ - a local progress value,
56
+ - a set of state responsibilities,
57
+ - one or more key poses,
58
+ - a handoff to the next state.
59
+
60
+ A chapter may span one DOM section, several sections, or no visible section at all. A single pinned stage can be controlled by invisible scroll spacers or semantic content regions while the visual composition remains continuous.
61
+
62
+ ## Four motion layers
63
+
64
+ ### Narrative motion
65
+
66
+ Narrative motion is authored and repeatable. It includes:
67
+
68
+ - camera blocking,
69
+ - major subject transforms,
70
+ - chapter transitions,
71
+ - typography entrances and exits,
72
+ - light and environment changes,
73
+ - SVG and mask choreography,
74
+ - route entrance and leave sequences.
75
+
76
+ GSAP timelines are usually the authoritative sequencer.
77
+
78
+ ### Ambient motion
79
+
80
+ Ambient motion communicates that the world is alive even when scroll stops:
81
+
82
+ - shader time,
83
+ - floating particles,
84
+ - breathing scale,
85
+ - subtle light fluctuation,
86
+ - cloth or fluid simulation,
87
+ - model idle animation,
88
+ - environmental noise.
89
+
90
+ Ambient motion should usually continue independently of chapter progress.
91
+
92
+ ### Reactive motion
93
+
94
+ Reactive motion reflects immediate input:
95
+
96
+ - pointer parallax,
97
+ - hover attraction,
98
+ - drag response,
99
+ - scroll-velocity distortion,
100
+ - device orientation,
101
+ - cursor proximity,
102
+ - gaze or focus changes.
103
+
104
+ Reactive motion is normally additive and limited. It must not destroy the authored composition.
105
+
106
+ ### Discrete state-transition motion
107
+
108
+ Discrete motion moves between named states rather than following continuous scroll:
109
+
110
+ - opening a project,
111
+ - changing routes,
112
+ - opening navigation,
113
+ - switching a product configuration,
114
+ - entering an immersive mode,
115
+ - playing an intro after loading.
116
+
117
+ These transitions should have explicit entry, active, and exit states.
118
+
119
+ ## One clock, several signals
120
+
121
+ There is exactly one frame clock. Lenis is advanced from `gsap.ticker`, ScrollTrigger reads from Lenis, and the R3F canvas is advanced from the same ticker (see [`../../references/setup.md`](../../references/setup.md) and [`../../references/webgl-r3f.md`](../../references/webgl-r3f.md)). Nothing in the experience runs its own `requestAnimationFrame` loop. Scroll progress and elapsed time are different signals read on that one clock, not different clocks.
122
+
123
+ At minimum, distinguish:
124
+
125
+ ```ts
126
+ type ExperienceSignals = {
127
+ globalProgress: number; // 0..1 across the complete narrative
128
+ chapterProgress: number; // 0..1 inside the active chapter
129
+ elapsedTime: number; // continuous real time
130
+ deltaTime: number; // frame duration
131
+ scrollVelocity: number; // signed and clamped before visual mapping
132
+ scrollDirection: -1 | 0 | 1;
133
+ pointerX: number; // normalized -1..1
134
+ pointerY: number; // normalized -1..1
135
+ pointerSpeed: number;
136
+ };
137
+ ```
138
+
139
+ Do not drive shader life, model idle motion, and all micro-movement from scroll progress. A world that becomes completely static when scrolling stops feels like a timeline inspector, not an environment.
140
+
141
+ ## One writer per property
142
+
143
+ When GSAP, `useFrame`, pointer handlers, and Motion all write to `rotation.y`, the result depends on execution order and timing. That is not choreography; it is a race condition.
144
+
145
+ Resolve it through ownership:
146
+
147
+ ```text
148
+ subjectPlacementRig.rotation.y ← narrative timeline
149
+ subjectReactionRig.rotation.y ← pointer modulation
150
+ subjectIdleRig.rotation.y ← procedural idle
151
+ model.rotation.y ← model's own baked animation only
152
+ ```
153
+
154
+ The final world rotation is the composition of the hierarchy.
155
+
156
+ For scalar values that must combine, use explicit channels:
157
+
158
+ ```ts
159
+ finalDistortion =
160
+ narrativeDistortion +
161
+ velocityDistortion +
162
+ hoverDistortion;
163
+ ```
164
+
165
+ Clamp and shape the result before sending it to a shader.
166
+
167
+ ## Experience orchestration
168
+
169
+ The orchestrator is not necessarily one class. It is the system responsible for:
170
+
171
+ - knowing the active route and chapter,
172
+ - mapping input to progress,
173
+ - activating or disabling interaction envelopes,
174
+ - coordinating child timelines,
175
+ - exposing state to DOM and WebGL,
176
+ - handling resize and refresh,
177
+ - disposing route-specific systems,
178
+ - providing debugging and tuning information.
179
+
180
+ A useful conceptual pipeline is:
181
+
182
+ ```text
183
+ raw input
184
+ → normalized input
185
+ → experience state
186
+ → narrative timelines
187
+ → ambient/reactive systems
188
+ → final transform and material resolution
189
+ → render
190
+ ```
191
+
192
+ ## Professional polish comes from relationships
193
+
194
+ A single spectacular shader does not make an experience coherent. Professional polish is created by relationships:
195
+
196
+ - the camera stops just before the copy reaches full contrast,
197
+ - the object begins preparing the next transition before the current chapter ends,
198
+ - pointer influence reduces as the narrative move becomes critical,
199
+ - a light source reveals the shape that becomes the next chapter's mask,
200
+ - an outgoing word shares scale or trajectory with the incoming subject,
201
+ - sound accents occur at perceptual beats rather than arbitrary timeline times,
202
+ - reverse scroll reconstructs the previous state without a jump.
203
+
204
+ Think in terms of cause, anticipation, overlap, and consequence.
205
+
206
+ ## The minimum viable cinematic system
207
+
208
+ Before adding advanced effects, establish:
209
+
210
+ 1. one persistent stage,
211
+ 2. one central state model,
212
+ 3. one complete chapter,
213
+ 4. one complete handoff,
214
+ 5. named camera shots,
215
+ 6. property ownership,
216
+ 7. semantic timeline labels,
217
+ 8. responsive and reduced-motion behavior,
218
+ 9. a tuning surface,
219
+ 10. cleanup on unmount and route change.
220
+
221
+ A simple system with these qualities will feel more professional than a dense collection of disconnected effects.