$npx -y skills add heygen-com/hyperframes --skill seam-craftRender-correctness doctrine for scene-to-scene seams in HyperFrames launch videos — the prerequisites that make transitions composite correctly on the master timeline. Load when assembling the master timeline / index.html, when a white flash appears at a cut or crossfade seam (es
| 1 | # Seam Craft — render prerequisites for scene-to-scene transitions |
| 2 | |
| 3 | This is the **render-correctness doctrine** for PLV scene-to-scene seams: the |
| 4 | prerequisites and master-timeline mechanics that make any transition composite |
| 5 | correctly, independent of which specific transition is chosen. The per-transition |
| 6 | catalog (crossfade, push-slide, zoom-through, cut-the-curve, …) lives in the |
| 7 | transition registry — this page is the doctrine that sits underneath all of them. |
| 8 | |
| 9 | The transitions this doctrine governs are **Tier-B-ready**: pure transform / opacity / |
| 10 | filter on the two scene **clip wrappers** (`#el-<sid>`), no injected overlay DOM, no |
| 11 | per-scene cooperation. Overlay families (staggered blocks, blinds, light leak, grid |
| 12 | dissolve, page burn) and shader transitions are deferred to later phases. |
| 13 | |
| 14 | ## Stage ground prerequisite (white-flash guard) |
| 15 | |
| 16 | Several templates open a window where the two wrappers' summed opacity < 1 (the |
| 17 | cut-the-curve mid-window cut, zoom-through's 0.15 floor, plain crossfade's |
| 18 | power-curve dip). Whatever is BEHIND the wrappers shows through during that |
| 19 | window. If the assembled `index.html` `#root` has no opaque background, the |
| 20 | renderer composites the dip over its default **white** page → a white flash at |
| 21 | every seam, glaring on dark films (observed on two Spotify runs before the fix). |
| 22 | **The assembler must paint the stage:** `#root { background: |
| 23 | var(--canvas-deep, var(--canvas, #000)) }` — `assemble-index.mjs` now emits this; |
| 24 | any other consumer of these templates owns the same guarantee. |
| 25 | |
| 26 | ## How the injector applies a transition |
| 27 | |
| 28 | At a `break` boundary between scene _i_ (`from`) and scene _i+1_ (`to`), the |
| 29 | injector: |
| 30 | |
| 31 | 1. Extends `#el-<from>` wrapper `data-duration` by `duration_s` (holds its final |
| 32 | frame — verified: `core/src/runtime/init.ts:1393-1410` external-slot branch). |
| 33 | 2. Pulls `#el-<to>` wrapper `data-start` earlier by `duration_s` (creates the |
| 34 | overlap window). |
| 35 | 3. Reassigns **all** clip `data-track-index` as a 0/1 ping-pong so the two |
| 36 | overlapping wrappers never share a track (same-track overlap is illegal — |
| 37 | `core/src/lint/rules/composition.ts`). Higher track composites on top. |
| 38 | 4. Stamps the `gsap_template` into `window.__timelines["main"]` at `T = overlap-start`. |
| 39 | |
| 40 | Verified by prototype render (2026-05-31): the master-timeline wrapper tween is |
| 41 | seeked and rendered (no double-seek with the sub-comp's own paused timeline — |
| 42 | the runtime drives them independently), the extended wrapper holds scene _i_'s |
| 43 | final frame, and the higher-track incoming wrapper composites over + blends with |
| 44 | the outgoing one. |
| 45 | |
| 46 | ## Template placeholders |
| 47 | |
| 48 | The injector substitutes these tokens in each `gsap_template` line: |
| 49 | |
| 50 | | Token | Meaning | |
| 51 | | ---------------------------------- | ------------------------------------------------------------------------ | |
| 52 | | `__OLD__` | `"#el-<from>"` — outgoing clip wrapper selector (quoted) | |
| 53 | | `__NEW__` | `"#el-<to>"` — incoming clip wrapper selector (quoted) | |
| 54 | | `__T__` | overlap-start time in seconds (master clock) | |
| 55 | | `__DUR__` | `duration_s` for this boundary | |
| 56 | | `__DX__` | horizontal travel for directional types: `-1920` (LEFT) / `1920` (RIGHT) | |
| 57 | | `__DY__` | vertical travel: `-1080` (UP) / `1080` (DOWN) | |
| 58 | | `__ORIGIN_OUT__` / `__ORIGIN_IN__` | transformOrigin pair for `squeeze` | |
| 59 | |
| 60 | `filter` / `scaleX` / `transformOrigin` are lint-clean on the master timeline |
| 61 | (verified: `core/src/lint/rules/gsap.ts` has no per-property whitelist and scopes |
| 62 | its checks to `data-composition-id` ranges; the x/y/scale/rotation/opacity |
| 63 | whitelist is a _scene-worker_ prompt rule only — it does not bind index.html). |