feat(skills): product-launch-video skill + consolidate motion knowledge into hyperframes-animation (#1745)

* feat(skills): product-launch-video + consolidate motion knowledge into hyperframes-animation

- Add the product-launch-video skill: shot-sequence architecture where each
  visual frame is a time-coded shot sequence picked from a blueprint menu and
  paced to the voiceover (anti-PowerPoint). Includes the frame-worker sub-agent,
  story/visual/motion-design references, and audio/captions/transitions/
  stage-assets/assemble-index scripts.
- Consolidate motion knowledge in hyperframes-animation as the single source of
  truth: promote the updated atomic rules (31 -> 36) and rename product-launch-
  video's archetypes into hyperframes-animation blueprints (13 -> 15, replacing
  the old set). product-launch-video, faceless-explainer, and pr-to-video now
  reference them via ../hyperframes-animation/{rules-index,blueprints-index}.md
  and the rules/blueprints dirs. Fixes the discrete-text-sequence broken links;
  blueprints no longer ship per-id runnable examples, so example references in
  the consumers were dropped.
- Default HeyGen TTS voice to Marcia (deterministic; was the API's first English
  voice, which drifts on catalog re-sort). Override with --voice.
- assemble-index pre-assembly frame guards: auto-repair a sub-comp root missing
  canvas dims; hard-fail on <video>/<audio> inside a sub-comp; hard-fail on a
  timed non-root element missing class="clip" or overlapping same-track clips.
- Lint/CLI: lint media inside sub-compositions as an error; stop false-positive
  caption layout/lint findings; contrast/layout-audit skip elements hidden by an
  invisible ancestor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(skills): clear CodeQL alerts in assemble-index.mjs

- script/style blanking regex now matches closing tags with trailing
  whitespace (</script >, </style >) — js/bad-tag-filter (high).
- drop the existsSync precheck before reading/repairing a frame file; read
  directly and handle ENOENT, removing the check->write TOCTOU window —
  js/file-system-race (high).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
WaterrrForever
2026-06-27 02:51:35 +08:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 413d8187fd
commit 05af482f22
67 changed files with 3838 additions and 3006 deletions
@@ -1,119 +0,0 @@
# Composition — product-launch visual-design judgment
> The composition-judgment layer for **Step 4 (Visual design)**. You read it while enriching `STORYBOARD.md` frames: which layout, how much frame the hero fills, how many depth layers — **director decisions**. Concrete px (safe margins 96-150), scale (1.05 / 0.92), three-layer `box-shadow`, `perspective` values are the **frame worker's** job; you name the intent in the frame's composition note. Video composition is closer to film / poster design than webpage layout — no scrolling, no reflow; every frame is a fixed canvas, every pixel matters. Default canvas **1920×1080**; portrait `1080×1920` / square `1080×1080` per the storyboard `format`.
## Squint test
Squint (or blur the frame). Can you still pick out the most important element, the second, and clear spatial groups? If everything has equal weight after blur, hierarchy is broken — redesign before writing the note. The strongest frames pass this: one dominant block + one supporting structural element, everything else demoted.
## Canvas zones (conceptual)
```
+--------------------------------------------------+
| Optional top chrome |
| +----------------------------------------------+ |
| | Safe margin | |
| | +----------------------------------------+ | |
| | | Primary content area | | |
| | | (center 65-75% of frame) | | |
| | +----------------------------------------+ | |
| | | Caption band (bottom ~17%, HARD w/ captions) | |
| | +----------------------------------------+ | |
| +----------------------------------------------+ |
+--------------------------------------------------+
```
- **Top chrome** — only for workspace-mockup frames; otherwise skip.
- **Safe margin** — key content stays off the edges; hero / editorial frames need more air.
- **Primary content area** — the center 65-75% is where the eye rests; body text never presses the edge.
- **Caption band (bottom ~17%, HARD-reserved when captions are on)** — when the film has captions enabled (the frame's `Captions:` flag), the bottom ~17% of **canvas height** is reserved (landscape 1080h → bottom 180px, y 900-1080; portrait 1920h → bottom 320px, y 1600-1920): primary content and key visuals **cap at the band top**, and a centered hero anchors at **y ≈ 0.42 × height** (landscape ≈454, portrait ≈806), not the canvas midpoint. Background / ambient / surface layers are exempt and may stay full-bleed. Captions disabled → the zone is usable, but keep it clear anyway for bottom-edge consistency across frames.
You write "hero word centered with generous safe margins"; you do not write `padding: 150px 120px 92px`.
## Portrait & square (non-16:9 canvases)
The zones, density, hierarchy, and depth principles all still apply; the **aspect ratio** changes, and a wide-frame layout does not transplant into a tall one. Design for the storyboard's `format` from the start — never plan landscape and "crop."
- **Stack vertically, not side-by-side.** Portrait has little horizontal room: split-screen / triptych / 60-40 asymmetry become **top/bottom stacks**, vertical step lists, stacked bands. Square tolerates side-by-side only for two compact items.
- **Vertical center moves with the canvas** — anchor a centered hero around **y ≈ 0.42 × height** (portrait ≈806, square ≈454), not a fixed 540.
- **Type runs larger, fewer words per line** — narrow frames wrap long headlines badly; prefer short kinetic lines, bigger type, more vertical rhythm.
- **Travels well to portrait:** Centered, Layered Depth, Full-Width Strip (now a stacked band), vertical Rule-of-Thirds. **Avoid** wide Split Screen and Triptych — use stacked equivalents.
- **Density still rules** — primary visual ≥ 40% of canvas, ≥ 3 depth layers, measured against the tall frame; an empty top or bottom third reads as placeholder.
- **Real 16:9 screenshots don't fit a tall frame** — never letterbox with dead bars, never stretch-distort. Instead: crop to the salient region; seat it as a top/bottom band with kinetic type filling the rest; or scale it inside a device / browser-frame mock.
## 7 composition templates
Use ≥3 different templates per video (5 frames → 3+, 9 frames → 4+). **Don't default every frame to centered**; never use the same layout class twice in a row.
1. **Centered (hero / climax)** — one dominant element, generous breathing room. Brand reveal, key metric, CTA, climax.
2. **Rule of thirds** — anchor on a thirds intersection; remaining space carries support or negative space. Feature showcase, demo + description.
3. **Split screen (comparison / dual focus)** — left/right halves carry separate elements. Before/after, comparison, problem/solution.
4. **Layered depth (immersive)** — foreground / midground / background differ in scale + opacity. Opening hooks, atmosphere, workbench beats.
5. **Asymmetric (editorial)** — primary content pushed to one side (60/40, 70/30); intentional imbalance → tension + sophistication. Feature focus, dense info, editorial proof.
6. **Triptych (three-panel)** — three equal zones for three capabilities / beats at once.
7. **Full-width strip** — one horizontal band (ticker, logo chain, marquee), usually ~20% of canvas height.
## Frame density — avoid empty frames
Common failure: small elements floating in the center with empty space around them. Every frame must feel **intentionally filled**.
- **Primary visual occupies ≥ 40% of canvas** — hero text 50-75% height × 60-80% width; a centered card 30-50% × 50-70%.
- **≥ 3 visual layers** — background (gradient / particles / grid) + midground (main content) + foreground (emphasis / decoration).
- **Openings and closings** are prone to emptiness — black background + a lonely line of text reads as placeholder. Add environmental layers: dual-radial swell, floating particles, brand-color ambient texture, low-opacity scanlines.
- **Text-only frames still need visual elements** — logo, an asset, icon decoration, halftone field, brand-derived geometry.
- **Feature the frame's `asset_candidates`** — story already chose the real assets for this frame (they're the highest-value material); a product screenshot at 60% + supporting text + an ambient layer feels full; the text alone feels empty.
**Fullness test:** could this frame stand as a poster or social graphic? If it looks like a sparse slide → add layers.
## Negative space
Whitespace directs attention, it isn't waste. Tight grouping (icon + label) → small spacing; unrelated groups → large separation; asymmetric outer margins feel more designed than equal padding; a hero word keeps large side whitespace so one word carries the weight. **Failure modes:** everything equidistant (no grouping); unintended overlap; text tight against an edge; captions colliding with bottom visuals; the framework's default padding everywhere.
## In-frame visual hierarchy
Visual weight, strong → weak: **large image** **motion** (moving beats static) **high contrast** **type scale** **position** (center + upper third are golden). Combine **at least two** — an element that is large, moving, and upper-third is unquestionably primary.
A title that is only _larger_ (sharing weight/color/spacing with body) reads weak. Stack dimensions:
| Dimension | Strong contrast |
| --------- | ------------------------------------------- |
| Size | 3:1 ratio or larger |
| Weight | 800-900 vs 400 |
| Color | high contrast against background |
| Motion | one element moving vs all else static |
| Position | top / left = primary |
| Space | large surrounding whitespace vs equidistant |
## Cards and grouping
Spacing + alignment can group without a card container. **Use cards** when content is genuinely distinct, when a UI-demo group is independently actionable (command row, feature card), or when shadow-stacking communicates "lifted." **Don't** card for mere separation (use whitespace) or for a continuous list. **Never nest cards** — claustrophobic, muddy hierarchy. You write "comparison-split: left/right dual cards, three-layer shadow stack"; the worker writes the `box-shadow`.
## Asset prominence
The frame's assets (logos, product images, screenshots) are real and strongly brand-related — feature them, don't shrink them into decoration. A focal product screenshot fills **40-60% of the frame**; a logo stays recognizable at playback size; use the highest-quality version. **Never** replace a real asset with a generic decorative graphic when a real one exists.
## Depth on a 2D canvas
Layer **2-3 depth techniques** per frame to avoid a flat poster (concrete perspective / rotate / scale values are the worker's):
| Technique | Effect |
| ---------------- | ------------------------------------------------------------------------------------------ |
| Size difference | larger = nearer, smaller = farther |
| Blur | blurred = background, sharp = foreground |
| Opacity gradient | low = receding, full = primary |
| Overlap | foreground partially covers background |
| Shadow stacking | three-layer shadow = lift + brand feel |
| Motion speed | faster parallax = closer |
| Counter-scale | camera pushes toward focus → background appears larger, focal CSS scale <1 but fills frame |
You write "3 depth layers: background swell + midground product + foreground CTA glow; background counter-scales for the push"; the worker writes the scale values.
## What should not appear
Nav bars, footers, cookie banners, scrollbars, cursor arrows, browser chrome, unclickable buttons, generic decorative shapes replacing real assets, floating bokeh / purple-to-blue AI gradients (the "default AI cliché," banned). **Exception:** an intentional product-interface reconstruction for a UI-demo frame — nav bars, command panels, timeline tracks, CTA buttons give real context, making it read as a real workflow.
## Composition note example
> "Composition: asymmetric 60/40 — product screenshot occupies left 60%, copy + CTA right 40%. Generous safe margin; text capped inside the primary content area. 3 depth layers: background swell + midground screenshot + foreground CTA glow. Density: primary visual ~55%, ambient adds 5% scanline + architectural grid."
One line per frame; never concrete px / scale / shadow recipes (the worker writes those).
@@ -0,0 +1,220 @@
# Cut catalog — within-frame seams (worker-built)
> **A worker build-recipe (Step 5) — the sibling of `../hyperframes-animation/rules/`, not a second motion doc.** These are within-frame cuts the **frame worker builds INSIDE its own composition** (Z-scale + blur + opacity tweens, or per-word x-staggers, all on the frame's own paused GSAP timeline). They are **not** the between-frame transition: story owns that via `transition_in`, which the harness's injector stamps from a **separate registry vocabulary** (`crossfade` / `blur-crossfade` / `push-slide` / `zoom-through` / `squeeze`) — the catalog names here (**cut-the-curve / inverse-zoom / waterfall**) are **not** valid `transition_in` values. Use this catalog when a frame's shot sequence has an internal seam — a within-scene text/element swap, a **Scene-to-Scene** cut (a `Scene` is a time window WITHIN one frame, **not** a frame-to-frame boundary), or a text-to-text line change — and you want it to read as one continuous move instead of a hard slideshow cut. (`zoom-through` lives in both worlds: a whole-frame wrapper transition in the registry, an element-level Z-cut here — same idea, different scope.)
Four techniques that create depth and continuity:
1. **Zoom-Through** — within-scene text swaps, Z-axis, moving TOWARD the viewer
2. **Inverse Zoom-Through** — Z-axis swaps moving AWAY from the viewer
3. **Cut the Curve** — between-scene transitions on x/y
4. **Waterfall Cut** — word-by-word cut-the-curve with staggered exits and entries
All four are the same underlying principle: **cut at peak velocity, match direction and
speed on both sides of the cut.** The differences are axis, scope, and granularity.
**Choosing which at a seam:** for an UNFINISHED phrase (building one larger idea across
several visually distinct scenes that still approach the same point — multi-line text, a
run of consecutive cards) use **cut-the-curve** / **waterfall**. For a STATE CHANGE (turning
to a NEW part of the video — most often hook → context, between two distinct chapters) use
**zoom-through**, and **inverse zoom-through** for an arrival / payoff beat. Chain these so
the frame's internal seams feel like one camera moving through the content.
---
## Blur Logic (applies to all Z-axis variants)
Blur sells the speed at the cut, but it must scale with the SUBJECT SIZE:
| Subject | Peak blur | Why |
| ------------------------------------------------------ | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Text-scale (headline, line, word group) | **10px** | At 20px text smears into illegibility — the eye loses the word it was tracking and the cut reads as a glitch, not speed. (Learned 2026-06-10: the until-now inverse zooms shipped at 20px and read mushy; 10px keeps letterforms readable mid-cut.) |
| Full-frame surface (terminal window, card, screenshot) | **1820px** | Big surfaces have edges and texture that survive heavy blur; lighter blur on a full-frame move reads as a rendering hiccup instead of motion. |
Both sides of a cut use the SAME peak blur — the value must match at the swap frame.
Apply blur to the WRAPPER, never to individual children.
---
## 1. Zoom-Through (forward)
### The Problem
Text enters, holds, exits. Then next text enters, holds, exits. Each text block is
independent — no depth, no continuity. The video feels like a slideshow.
### The Principle
A velocity-matched cut on the Z-axis. You **never see both texts at the same time.** The
outgoing text scales toward the viewer (accelerating), blur and opacity peak at the cut
point hiding a hard swap, and the incoming text continues scaling up from behind
(decelerating into the focal plane). One continuous forward motion, two different texts.
### The Three Phases
**Phase 1: Exit** — text accelerates forward (toward viewer)
- Scale: `1.0 -> 1.2`, Blur: `0px -> 10px` (text-scale; see Blur Logic), Opacity: `1.0 -> 0.15`
- Scale/blur easing: `power3.in` (steep acceleration)
- Opacity easing: `none` (linear — even dimming, separated from scale)
- Duration: 0.2s
**Phase 2: Hard cut** at peak velocity + peak blur
- Outgoing: `opacity: 0` (instant via `tl.set`)
- Incoming: `opacity: 0.15, scale: 0.75, blur: 10px` (instant via `tl.set`)
- All properties match at the cut: blur, opacity, and scale DIRECTION (both scaling up)
**Phase 3: Entry** — text continues forward (growing into focal plane)
- Scale: `0.75 -> 1.0`, Blur: `10px -> 0px`, Opacity: `0.15 -> 1.0`
- Easing: `expo.out` (steep initial burst matching exit velocity, long settle)
- Duration: 0.5s
### Why Opacity Must Be Separate on Exit
Scale uses `power3.in` but that keeps opacity near 1.0 for most of the tween. Splitting
opacity to its own tween with linear ease makes the dimming even. On entry, all properties
can share `expo.out`.
---
## 2. Inverse Zoom-Through (backward)
The mirror: the camera "pulls back" instead of pushing through. The outgoing element
RECEDES away from the viewer; the incoming element arrives OVERSIZED (as if it had been
just behind the camera) and retracts into the focal plane. Both move in the shrinking
direction — same-direction rule preserved, just reversed.
**When to use over the forward variant:** arrival beats. The incoming element lands with
presence because it comes from larger-than-frame — right for a payoff line ("That changes
today."), a giant reply, or a held end-state. Forward zoom-through reads as _progressing
through_ content; inverse reads as _arriving at_ content.
### The Three Phases
**Phase 1: Exit** — element recedes (away from viewer)
- Scale: `1.0 -> 0.8`, Blur: `0px -> 10px` (text-scale)
- Scale/blur easing: `power3.in`; Opacity: `1.0 -> 0.15` on `none` (separate tween)
- Duration: 0.2s
**Phase 2: Hard cut**
- Outgoing: `opacity: 0` via `tl.set`
- Incoming: `opacity: 0.15, scale: 1.25, blur: 10px` via `tl.set`
**Phase 3: Entry** — incoming retracts into place
- Scale: `1.25 -> 1.0`, Blur: `10px -> 0px`, Opacity: `0.15 -> 1.0`
- Easing: `expo.out`, Duration: 0.5s
Shipped examples: `boring → until-now` and `B3 → "That changes today."` (sfx-music-launch);
seams 3/4 in claude-paper (`follow-up → thinking`, `thinking → compose UI`).
---
## 3. Cut the Curve (Scene Transitions)
### The Principle
Use cut-the-curve for **all scene-to-scene transitions** on x and y axes. The outgoing
scene's hero element accelerates in one direction, the cut lands mid-motion, and the
incoming scene's hero element continues moving in the **same direction** and decelerates.
Nothing exits fully off-screen and nothing enters from fully off-screen — **speed plus
opacity fading trick the eye**; the partial moves are enough.
### Same Path, Same Direction
If Scene A's hero slides left, Scene B's hero enters from the right and continues sliding
left. Both move leftward. One continuous motion.
| Direction | Scene A exit | Scene B entry start | Scene B entry end |
| --------- | -------------- | ------------------- | ----------------- |
| Leftward | `x: 0 -> -230` | `x: +230` | `x: 0` |
| Rightward | `x: 0 -> +230` | `x: -230` | `x: 0` |
| Upward | `y: 0 -> -230` | `y: +230` | `y: 0` |
| Downward | `y: 0 -> +230` | `y: -230` | `y: 0` |
### Velocity matching via mirrored eases
The cleanest match: exit `power4.in` and entry `power4.out` with the SAME distance and
duration — mathematically the two halves of one `power4.inOut` composite, so the entering
element picks up at exactly the 50% point of the notional path at identical velocity
(e.g. 230px / 0.3s ≈ 3,070 px/s at the cut on both sides).
The fade trick: the exit's opacity completes at ~2530% of its travel (fade duration
≈ 0.180.3s vs motion 0.30.34s) — the element vanishes while still visibly accelerating,
and nothing has to reach the frame edge. Entry fades IN fast from ~0.35 under its
deceleration. Time the LAST fading element to die right at the hard cut — gaps where
nothing is moving read as awkward dead air.
### Rules
- Use cut-the-curve for all scene transitions — it's the default, not an accent
- Same direction on both sides; mirrored `.in`/`.out` eases, same distance + duration
- Exit duration short (0.20.4s), entry duration >= exit duration
- Partial travel + fade, never full off-screen moves
---
## 4. Waterfall Cut (word-by-word cut-the-curve)
Cut-the-curve at WORD granularity — the strongest version of the leftward cut for
text-to-text seams. Each word of the outgoing line ramps out on its own pronounced curve;
each word of the incoming line cascades in mid-flight. The stagger turns the cut into a
wave the eye rides across the seam.
### Exit (per word)
- Motion: `x: 0 -> -230` over 0.34s on **power4.in** — a much more pronounced ramp than
the usual power2: the word barely creeps, then RIPS
- Fade: `opacity -> 0` over 0.18s (separate tween, `power1.in`) — completes when the word
is only ~2530% into its travel
- Stagger: reading order, ~0.022s per word, timed so the LAST word finishes fading right
at the hard cut
### Entry (per word)
- `fromTo x: +230 -> 0, opacity: 0.35 -> 1` over 0.3s on **power4.out** — the mirrored
back half of the composite; every word ignites already moving at matched velocity
- Waterfall stagger with SHRINKING gaps (start 0.05s, multiply by ~0.84 per word) so the
cascade accelerates across the line — the cascade should speed up word over word, not run
at a flat per-word delay
- Pre-set all words to `x: +230, opacity: 0` at build time — `immediateRender: false`
alone leaves un-started words sitting visible at rest during the stagger window
### Whole-line variant
A single-line beat (e.g. a big intro line) exits as one group with the same pronounced
ramp, but stretch its fade to ~0.3s ending ~0.02s before the cut — a lone element that
fades early leaves dead air that a word cascade would have covered.
Shipped example: `until-now.html` B1→B2→B3 (sfx-music-launch).
---
## Choosing a Variant
| | Zoom-Through | Inverse Zoom | Cut the Curve | Waterfall Cut |
| -------------- | --------------------------- | --------------------------- | ----------------- | ------------------------- |
| Scope | Within-scene text swap | Arrival/payoff beat | Between scenes | Text-to-text seam |
| Axis | Z, toward viewer | Z, away from viewer | X / Y | X, per-word |
| Peak blur | 10px text / 20px full-frame | 10px text / 20px full-frame | none required | none (fade does the work) |
| Opacity at cut | 0.15 | 0.15 | exit faded by cut | last word dies at cut |
| Feel | progressing through | arriving at | carried sideways | a wave across the seam |
---
## Anti-Patterns
| Don't | Why | Instead |
| ---------------------------------------- | ------------------------------------------- | ------------------------------------------------------ |
| Two texts visible during a zoom-through | Overlapping text breaks the Z-axis illusion | Hard cut at blur peak, one text at a time |
| 20px blur on text-scale subjects | Letterforms smear; reads as a glitch | 10px for text, 1820px only full-frame |
| Elements on different paths across a cut | Eye tracks one direction, cut goes another | Same property, same direction |
| Mismatched blur/opacity at the swap | Visible flash or brightness jump | Identical values at the cut frame |
| Gentle easing on entry (`power2.out`) | Entry velocity feels slower than exit | Mirror the exit: `power4.out` / `expo.out` |
| Full off-screen exits / entries | Wastes time and breaks the speed illusion | Partial travel + early fade |
| Lone element fading long before its cut | Dead air at the seam | Fade ends ~0.02s before the cut, or use a word cascade |
| Zoom-through on body text | Small text at 0.75 scale is unreadable | Only headlines and short phrases |
| Scene cuts without cut-the-curve | Static cuts feel like a slideshow | Cut-the-curve is the default |
@@ -1,139 +1,156 @@
# Motion language — product-launch visual-design judgment
# Motion language — the move vocabulary + the motion doctrine + the seek-safe core
> The motion-judgment layer for **Step 4 (Visual design)**. You name **each shot's choreography, spring intent, beat rhythm, holds, stillness, and the idle-motion budget** while enriching `STORYBOARD.md` frames; the **frame worker** maps intent to concrete GSAP eases / ms / stagger / code (via `hyperframes-animation`). A good promo feels like one continuous whole — one camera, one spring feel, **every shot directed across its full length** — not a pile of slides that animate once and freeze. You reference motion by **role**, never by curve: eases / durations resolve from `frame.md`'s motion tokens, named `entry` / `emphasis` / `exit` / `drift` (the pack's exact keys may differ); the worker maps the curve. Between-frame **transitions are not yours** story names `transition_in`, the harness injects it.
> The motion layer for **Step 4 (Visual design)**. When you write a frame's **time-coded shot sequence**, you name each scene's move **inline from the vocabulary below** — a named palette of the moves the golden corpus actually uses. Each move carries the **backing rule id** in this skill's local `../hyperframes-animation/rules/`; cite that id so the move resolves to a real recipe when a **frame worker** implements it in Step 5 (the worker reads the rule body in `../hyperframes-animation/rules/<id>.md` — it reproduces the move, it does not guess from the name). You name motion by **role / move name**, never by raw GSAP curve, ms, or stagger formula — the worker maps the curve. Between-frame **transitions are not yours**: story names `transition_in`, the harness injects it; that injected transition **is** the frame's exit. For cuts a worker builds INSIDE a frame (within-scene swaps, scene-to-scene seams), see the catalog in `cut-catalog.md`.
## A frame is a shot, not a slide
A good promo feels like one continuous film — one camera, one motion feel, **smooth and timed to the voiceover** — not a pile of slides that animate once and freeze. The doctrine in Part 2 is load-bearing: when in doubt, do what it says.
The single failure that makes a promo read as PowerPoint: a frame whose content **animates in over the first ~0.8s, then freezes** for the rest of its duration while a slow drift plays underneath. The entrance is not the shot — it's the **first beat** of it. You direct the **whole duration**.
---
Three layers fill a shot, each governed by a different rule:
# Part 1 — the move vocabulary
| Layer | What | Rule |
| ------------------------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------- |
| **Camera** (macro) | ONE correlated move on the frame root — slow drift / dolly / push / parallax pan | **always on, the whole shot** — this is the "someone is filming this" layer |
| **Choreography** (action) | the beat develops: entrance → mid-shot move (reveal / rearrange / morph / emphasis hit) → settle | **fill the duration** — a shot animated only at entry is a slide |
| **Idle life** (texture) | ambient continuation on the 1-2 elements that hold a live slot — breathing, glow, float | **budgeted** — this is where screensaver lives; cap it |
Reach into this palette when naming a scene's motion. Pick the move that matches the beat, name it in the shot sequence, and cite the rule id after `→`. The blueprints (`../hyperframes-animation/blueprints/`) name these same moves in their `rule mapping`; you're drawing from one shared palette. Compose 24 across a shot's scenes (entrance → sequential reveal → settle), not all at once.
The reconciliation that matters: **mandate choreography, budget idle life.** Purposeful, sequential motion that carries information should fill the shot; ambient, simultaneous motion that carries none should be capped. Many elements each floating independently reads as _screensaver / UI animation_; a shot that only enters then freezes reads as _slideshow_. Avoid both — **one camera move + a directed multi-phase action + 1-2 living elements**, nothing scattered.
## Kinetic type
## Multi-phase choreography — direct the full shot
- **hard-cut / flash word-swap** — a word or line replaces the previous one on an instant cut (no fade/roll); the swap itself is the beat. → `discrete-text-sequence`
- **in-place token cycle** — a fixed line holds and only its variable slot changes, token → token → token. → `discrete-text-sequence`
- **per-word staggered reveal** — a phrase assembles word-by-word (or chunk-by-chunk), each landing on its own beat. → `dynamic-content-sequencing`
- **kinetic beat-slam** — short phrases slam in on a shared percussive beat array, each with a distinct entrance, resolving on a locked finale; the recipe for "punchy / rhythmic" taglines. → `kinetic-beat-slam`
Every non-still frame's timeline is choreographed across its length, not front-loaded into the entrance:
## Typewriter
```
entrance → development → settle
```
- **type-on with caret** — text types in character-by-character behind a blinking caret. → `discrete-text-sequence` (+ `context-sensitive-cursor` for the caret blink / color)
- **backspace-and-retype** — the line types, deletes the last word(s), and retypes a new one (typo-correction, reframe). → `discrete-text-sequence` (+ `context-sensitive-cursor`)
- **entrance** — the beat's primary content arrives (hero `entry` / `heavy`; groups staggered).
- **development (the phase that's usually missing → PPT)** — mid-shot, the content _does something_: a second element reveals, elements rearrange to a new layout, a card morphs to its next state, a count-up runs, an emphasis hit lands on the keyword. This is the motion that separates video from slides.
- **settle** — the shot resolves and holds for its read; the camera + idle life continue underneath (never a hard freeze).
## Count-up / data
**Architecture:** in hyperframes only the **exit** is forbidden mid-video (the frame unmounts; the harness transition _is_ the exit). Everything _before_ the settle — including rich mid-shot development — is free and seek-safe. Build the development phase; skip only the exit (unless you are the final frame).
- **value-scaled counter** — a number counts up and its font size grows with the value, so the climb itself escalates. → `counting-dynamic-scale`
- **bars / progress / star wipe** — a number paired with a graphic that fills: bar-height stagger, a progress bar / ring filling, a fractional star-rating wipe. → `stat-bars-and-fills`
When you name a **`blueprint`**, the development phases come from its recipe — write the composition note **shot-by-shot** to match. When you name **no blueprint**, the **≥3 cited effects ARE the phases** — sequence them (one enters, one develops, one emphasizes); **don't fire them all at t=0** (simultaneous entry collapses three phases into one slide).
## Reveal / decode
## Spring intent (by role, not curve)
- **3D char flip-decode** — characters flip in 3D and resolve from scrambled glyphs to the real text (decryption feel). → `hacker-flip-3d`
- **SVG self-draw** — an outline / icon / ring draws itself stroke-by-stroke. → `svg-path-draw`
| Intent | Feel | Use |
| ---------- | ------------------------------------------- | ------------------------------------------------- |
| **entry** | confident slight overshoot, settles quickly | primary element entry (default) |
| **gentle** | soft slide-in, no overshoot | background elements, subtle motion |
| **snappy** | tight overshoot, nearly instant | UI elements, small icons, buttons |
| **heavy** | weighted deceleration | large images, prototype screenshots, hero visuals |
| **slam** | bouncy overshoot, intentionally loud | logo / bell / impact moments |
## Camera
**Consistency:** similar elements share one intent (all icons `snappy`, all hero images `heavy`). Don't invent a unique ease + duration per element.
- **push / focus / drift** — a sequential camera move on the frame root (pull-back → focus → push) plus continuous micro-drift; the cinematic baseline. → `multi-phase-camera`
- **zoom-to-target** — zoom into a non-centered element (scale + counter-translate to keep it framed). → `coordinate-target-zoom`
- **pan / focus-lock** — a virtual camera transforming one `.world` wrapper to pan / zoom / lock onto a region. → `viewport-change`
- **camera-cursor-tracking** — the viewport locks to a moving focal point (a typing cursor), static framing then focal-locked tracking. → `camera-cursor-tracking`
**Forbidden:** `bounce.out` / `elastic.out` (dated; real objects decelerate, they don't bounce — low overshoot for `entry` is fine, high overshoot only for clearly playful moments); a unique ease+duration per element (visual noise).
## Layout motion
## Duration intent
- **cluster→outward expansion** — elements start clustered at center and expand outward to their final positions in lockstep. → `center-outward-expansion`
- **orbit** — elements flip in from 3D space and settle into a continuous elliptical orbit (entry flips in-place at the orbital position). → `orbit-3d-entry`
- **split-tilt cards** — two cards side-by-side with opposing rotationY tilts, entering from their respective sides (comparison / before-after). → `split-tilt-cards`
- **logo/avatar ring + connectors** — avatars or logos on an elliptical ring with SVG connection lines to a center point, staggered entry. → `avatar-cloud-network`
Reference by **tier** ("instant feedback" / "state change" / "layout change" / "entry animation"); the worker maps concrete ms / frames at 30fps. **A single entry should not exceed ~800ms** — for a longer buildup, use multi-element stagger or a development phase, **not** one long tween.
## Surface / UI
**Phase-to-phase within a shot is swift**when one element makes way for the next (development), the outgoing move runs ~75% of an entry; arrival is deliberate, hand-off is quick. (The between-frame **exit** is the harness's transition, never your within-shot motion.)
- **3D page-scroll reveal** — a full webpage as a tilted 3D card whose internal content scrolls to reveal specific sections. → `3d-page-scroll`
- **cursor click + ripple** — a cursor moves to a target, depresses with it on click, and emits an expanding ripple. → `cursor-click-ripple`
- **button press** — a tactile press: compression then spring recovery, optional release burst / glow. → `press-release-spring` (or `physics-press-reaction` for a click that compresses cursor + target together)
- **keyword glow** — keywords light up with glow + scale + color on an attack-decay-rest envelope, synced to a word rail. → `asr-keyword-glow`
## Stagger cap
## Morph / handoff
When staggering N elements, **total ≤ 500ms** (longer feels dragged):
- **scale-swap** — two elements at the same screen center hand off: the outgoing cluster shrinks + fades as the incoming one arrives. → `scale-swap-transition`
- **card morph-anchor** — a container morphs apparent size + corner radius + surface between two shots, then fades to reveal the real target beneath (HyperFrames uses uniform `scale`, not `width`/`height`). → `card-morph-anchor`
- **3-7 elements** — normal stagger, total 300-700ms.
- **8+ elements** — tighten per-item delay, or stagger only the first few and enter the rest with the last.
- Never let stagger run past 500ms.
## Seam cuts (worker-built, inside a frame)
## Beat structure across frames (the cross-frame rhythm)
The velocity-matched cuts a worker authors between a frame's own Scenes. Name the seam in the shot sequence; the recipe is in the catalog, not a single `../hyperframes-animation/rules/` id.
Rhythmic videos breathe: tension → release → tension → release. A clean reference shape for a ~46s video:
- **zoom-through / inverse zoom-through** — a within-scene swap on the Z-axis; forward reads "progressing through", inverse reads "arriving at" (payoff). → `cut-catalog.md`
- **cut-the-curve** — a scene-to-scene cut where both sides move the same direction at matched velocity. → `cut-catalog.md`
- **waterfall cut** — cut-the-curve at word granularity, a wave across a text-to-text seam. → `cut-catalog.md`
| Phase | Duration | Rhythm | Frame type |
| -------------- | -------- | ------------------ | ---------------------------------- |
| Slow setup | 6-10s | slow build | hero establish, VO not yet present |
| Fast montage | 6-10s | ~2s each | quick cuts every 1.5-2s |
| Process reveal | 12-18s | continuous, no cut | screen recording, real workflow |
| Closure | 3-5s | still, breathable | logo, URL, CTA |
## Emphasis / marker
Allocate motion by a frame's energy: **high-energy** (hook, CTA) → faster entry, tighter stagger, `snappy`, busier development; **breathable** (brand reveal, emotional beat) → slower entry, `gentle`, longer hold, minimal development; **data** (stat, feature) → medium rhythm, clean stagger, count-up as the development phase.
- **highlight / circle / burst / scribble** — a marker-drawn emphasis on a word or element: yellow highlight sweep, hand-drawn circle, radiating burst, scribble, or rough sketch-outline. → `css-marker-patterns`
## Hold time — read time, not freeze time
## Aliveness during a hold (use sparingly — see Part 2)
After an element enters it must stay long enough to read (the worker maps concrete frames). "Hold" means **don't cut early** — the camera + idle life keep playing underneath; it is never a hard freeze.
- **subtle jitter** — the sanctioned way to keep a settled frame alive: a small, low-amplitude positional/scale jitter on the held element. The motion-graphics trick that reads "alive" without reading "weak." → `sine-wave-loop` (low-amplitude register)
- **live SVG internals** — internal SVG parts move so an icon feels alive (rotating hands, oscillating blades, pulsing dots, dash-flow); fine because it's the subject doing something, not a card breathing. → `svg-icon-enrichment`
- **finite bounded ambient** — a single bounded breathe/drift on ONE held hero, only when genuinely needed; de-emphasized — prefer sequential reveal or jitter first. → `sine-wave-loop`
| Content | Minimum hold |
| ------------------------------------ | ------------ |
| display text (1-3 words) | ~1s |
| short sentence | ~1.5s |
| data / statistic | ~1.5s |
| product screenshot | ~2s |
| complex visual (diagram, comparison) | ~2.5s |
| hero / climax word | ~1-1.4s |
## The added moves — now backed by local rules
Narration shorter than the needed hold → the frame's `duration` should still give the visual its read time.
Five moves the golden corpus needs were added to this skill's `../hyperframes-animation/rules/`, rounding out the vocabulary above:
## Stillness before climax — the marked exception
- **depth-of-field / selective-blur** — blur the off-focus subset to spotlight the focal element → `depth-of-field-blur`
- **motion-blur streak** — directional velocity blur on a fast fly-in / camera push-through → `motion-blur-streak`
- **3D depth scatter-assemble** — glyphs/elements scatter into a tumbling 3D cloud, then reassemble → `depth-scatter-assemble`
- **spring-pop entrance** — the canonical entrance pop; default to a smooth long-tail settle, overshoot only when explicitly playful → `spring-pop-entrance`
- **ambient glow / bloom** — un-triggered soft glow blooming behind a static hero → `ambient-glow-bloom`
Archive signature: a **0.3-0.75s pause** between the major action and its confirmation / result — the silence builds tension before the landing. It lands **because the rest of the video is choreographed** — stillness is a contrast against motion, so it only reads when motion is the baseline (if every frame is already frozen, a pause carries nothing). **Allocate it to only 2-3 frames per video, named in the `## Video direction` block**, where the narration lands a payoff. Stamped on every frame it becomes a tic and flattens the rhythm. Name `stillness-before-climax` in that frame's motion note; even then the camera move continues (still ≠ frozen).
---
## The idle-life budget — what may move during the hold
# Part 2 — the motion doctrine (load-bearing)
The 1-2 elements that keep moving _after_ the development settles. This is the layer that, overdone, becomes screensaver — so it is **capped**, not mandated:
These four rules are the difference between a clip that reads as a serious launch video and one that reads as an agent-made PowerPoint. Follow them as written.
1. **Camera move** — always present (the macro layer above); it alone keeps everything coherently alive.
2. **At most 1-2 secondary live elements** — the ones carrying the beat (hero, CTA). Everything else holds.
3. Prefer **macro move + depth parallax** over many independent floats.
## 1. Smooth beats bouncy — `power3` is the default
Secondary-slot menu (formulas are the worker's): **multiplicative breathing** (hero — small ±2-5% on final scale) · **glow pulse** (CTA / click target) · **sine float** (one decorative cluster at most) · **rotational drift** (3D cards, hero logo) · **orbit** (surrounding icons; counts as the one decorative cluster) · **halftone breathing** (atmospheric frames).
Elements should use **long-tail decel curves that let them settle smoothly. `power3` is enough in most cases.** No bouncy, no overshoot, no `back.out` / `bounce.out` / `elastic.out` as a default.
Multiplicative breathing is the signature for a hero **that holds a live slot** — not stamped on every hero. **Minimum amplitude ±6px or ±2-5% scale** — a 3px micro-float doesn't count; the budget caps how many things idle, it does not license invisible motion.
Bouncy is the **#1 instant turn-off** in user-made Remotion / HyperFrames videos, and the agent almost never gets it right — it thinks bouncy adds emphasis, but it buys that emphasis at the cost of cleanliness. The serious launch-video shops feel the same. **Smooth always wins.** Overshoot is demoted to a **rare, explicitly-playful exception** (a consumer/fun logo slam, a deliberate bell-hit) — never the house style. Name the intent as a long-tail settle; the worker maps `power3` (or `expo.out` on a fast arrival). See `../hyperframes-animation/rules/spring-pop-entrance.md` — it now leads with the smooth settle.
## Seek-safe motion — intents that don't survive the renderer
## 2. Sequential reveal in the back ~50%, timed to the voiceover
The frame is a **paused GSAP timeline seeked frame-by-frame**, so some "continuous" intents from a real-time engine cannot render — **don't name them**:
This is the anti-PowerPoint mechanism — sharper than "put development in the middle."
- **No infinite / forever motion** — "particles loop endlessly," "logo rotates forever," "marquee scrolls on repeat." Idle life is a **finite tween over the hold** (breathe up then back), never `repeat`/`yoyo`.
- **No randomness or wall-clock** — `Math.random` particle fields, `Date.now` drift. Every motion is the same on every render; name deterministic motion only.
- **Entrance + development only** (exit = final frame only) — the cross-frame exit is the harness's transition.
- Express oscillation/breathing as a **bounded finite move**, not a loop; the worker writes it, but don't ask for a loop the timeline can't hold.
- **Don't dump everything on screen in the first ~25%** of the scene. Rushing all content in up front is exactly what forces the slideshow feel.
- **Reveal each piece — a line, a card, even an h1 — when the voiceover mentions it**, sequencing reveals across the **later ~50%** of the scene. Same amount of agent work, but the cut becomes coherent and gains rhythm.
- **Less is more.** Fewer things on screen, each arriving on its VO beat, beats a full canvas that animated once and froze.
## Forbidden — both failure modes
Practically: a frame's shot sequence front-loads almost nothing — the entrance carries only what the VO is saying at t=0, and the rest of the elements wait in the timeline for their spoken cue. A reveal maps onto a development-class move from Part 1 (`per-word staggered reveal`, `cluster→outward expansion`, a `count-up`, an `asr-keyword-glow` synced to the word rail).
**Slideshow (under-motion):**
## 3. No lazy breathing, no bad pan/push — "no motion over bad motion"
- Content animates in, then **freezes** for the rest of the shot (the PPT tell).
- Only the entrance is animated; the remaining duration is a frozen hold under a drift.
- The ≥3 cited effects **all fire at t=0** instead of sequencing into entrance / development / emphasis.
- No mid-shot development on a non-still frame.
The agent's two reflexive ways to fake "aliveness" both read cheap:
**Screensaver (over-motion):**
- **No lazy breathing.** Scaling cards/text up and down in a circular loop to look "alive" is the cheap tell. Don't reach for it.
- **No bad slow pan / push in the back half.** A slow pan or push on elements in the later ~50% of a scene **disrupts the viewer's sightline and causes eye discomfort** — it actively makes the frame worse, not better.
- **Every element** floating independently; idle motion with no information.
- More than 1-2 elements idling at once; scattered sine floats as the "aliveness."
- A 3px micro-float standing in for real motion.
The fix for both is the same: **stagger element reveals in time with the script** (rule 2). And the governing principle: **"I'd rather have NO motion than BAD motion."** A held, still frame is better than a frame kept "alive" by breathing or a drifting camera. The **only sanctioned aliveness** during a hold is **subtle jitter** — a small low-amplitude jitter that keeps a frame from feeling dead without looking weak (it's in Claude videos now). Everything else holds.
**Always:**
## 4. Internal seams are velocity-matched cuts
- `bounce.out` / `elastic.out`; a bespoke ease+duration per element; `repeat` / `yoyo`; all elements entering simultaneously (must stagger or sequence).
When a frame has an internal seam — a within-scene swap, a Scene-to-Scene cut, a text-to-text line change — make it a **velocity-matched cut**, not a hard slideshow cut: cut at peak velocity, match direction and speed on both sides. The catalog (the four techniques, the blur logic, and which to use when) is `cut-catalog.md`; the moves are listed under **Seam cuts** in Part 1.
## Motion note example
## One-line summary
> "Macro: slow dolly-in on the frame root across the whole beat. **Entrance** — hero enters `EASE.entry` (heavy); icons snappy-stagger (5 items, ~400ms). **Development** — icons rearrange into a row, then the result panel reveals beneath them. **Stillness-before-climax 0.6s** (allocated frame; only the dolly continues). **Settle** — result emphasis: text gentle entry + double-layer glow; idle hold with hero breathing ±3% as the one live element."
Smooth long-tail (`power3`) over bouncy; reveal sequentially in the back ~50% timed to the VO (not dumped in the first 25%); no lazy breathing and no bad slow pan/push — prefer stillness, with subtle jitter as the only aliveness; cut at peak velocity with matched direction/speed (→ `cut-catalog.md`).
One line for a single-shot frame; **shot-by-shot when the beat is multi-phase** (always, when you named a `blueprint`). Never concrete ease curves / ms / stagger formulas / JS — the worker writes those.
---
# Part 3 — the seek-safe core (hard rules)
The frame is a **paused GSAP timeline seeked frame-by-frame**, so some "continuous" intents from a real-time engine can't render — don't name them. These are non-negotiable regardless of doctrine.
- **No infinite / forever motion** — "particles loop endlessly," "logo rotates forever," "marquee on repeat." Any aliveness (the subtle jitter, a live SVG internal, a needed bounded ambient) is a **finite tween over the hold**, never `repeat` / `yoyo`.
- **No randomness or wall-clock** — no `Math.random` particle fields, no `Date.now` drift. Every render must be identical; name deterministic motion only (stagger and any variation derive from the element index).
- **Entrances use `fromTo`** — state the from-state explicitly so a seek to `t=0` lands the element correctly; never rely on a CSS-hidden start (it renders visible before the tween claims it, and flickers under seek).
- **No CSS `transition` / `@keyframes` for motion** — CSS animation runs on the browser clock, independent of the HF seek clock; it desyncs and flickers. Drive all motion inside the paused GSAP timeline.
- **Entrance + sequential reveal only — no mid-video exit.** The frame unmounts via the harness transition; that injected `transition_in` **is** the exit. Exit motion belongs only to the final frame. (Worker-built seam cuts in `cut-catalog.md` are within-frame, not the frame's exit.)
## Forbidden — the failure modes
**Slideshow (the primary failure):** everything dumped on screen in the first ~25%; content enters then freezes; nothing revealed on its VO cue. Fix with rule 2 (sequential reveal timed to the VO).
**Cheap aliveness:** circular breathing as "life"; a slow pan/push in the back half disrupting the eye; many elements floating independently as "motion." Fix with rule 3 (stillness + subtle jitter only).
**Bouncy:** `back.out` / `bounce.out` / `elastic.out` as the default entrance; hand-keyed overshoot. Fix with rule 1 (`power3` long-tail; overshoot only when explicitly playful).
**Always:** no `repeat` / `yoyo`; no `Math.random` / `Date.now`; no all-elements-entering-simultaneously (sequence or stagger).
## Naming motion in a shot — example
> Scene 1 (0.01.0s): solid field; hero headline enters via **per-word staggered reveal** (`dynamic-content-sequencing`) on a smooth long-tail settle (`power3`); slow **push** on the root (`multi-phase-camera`) holds steady — no back-half re-push.
> Scene 2 (1.03.0s): as the VO names each capability, five feature icons reveal **sequentially** via **cluster→outward expansion** (`center-outward-expansion`), then a **value-scaled counter** (`counting-dynamic-scale`) ticks up beneath them — the back-half reveal, timed to the script, not dumped at t=0.
> Scene 3 (3.04.2s): hold on the result; **keyword glow** (`asr-keyword-glow`) lands on the payoff word as the VO says it; settles and holds still — at most **subtle jitter** (`sine-wave-loop`, low amplitude) keeps it alive; no breathing, no drift.
Name the move + its rule id (or `cut-catalog.md` for a seam cut) per scene; let the worker pick curves, ms, and stagger — defaulting to `power3`.
@@ -1,267 +1,373 @@
# Story design — product launch video
Use this reference in Step 3 to write `STORYBOARD.md` and `SCRIPT.md` for a product launch, promo, feature reveal, or marketing video.
Step 3 of the product-launch flow. Output: `STORYBOARD.md` (the narrative plan, one frame per beat) and `SCRIPT.md` (the locked spoken narration).
This file defines the story: what the video says, in what order, and why each frame exists. It does not define layout, visual effects, animation, or final markdown schemas. For exact file syntax, follow `../hyperframes-core/references/storyboard-format.md` and `../hyperframes-core/references/script-format.md`.
This step decides **what the video says, in what order, and how each beat is said** — and it says each beat in the SHAPE of a proven script. It does not design layout, composition, or motion (that is Step 4). For exact file syntax follow `../hyperframes-core/references/storyboard-format.md` and `../hyperframes-core/references/script-format.md`.
## What story design produces
For every beat, four things:
1. **Position in the SEQUENCE** — the shot order. Story truth decides which beats exist and in what order (the arc).
2. **Voiceover written in a blueprint's script shape** — the spoken line, drafted to sound like the proven script for the shape this beat is reaching toward (see the script bank below).
3. **A candidate `blueprint:` id** — the proven shot SHAPE this beat leans toward (a tag, not a commitment; Step 4 confirms or overrides).
4. **`transition_in`** — how this beat enters from the one before it.
The big idea: **the blueprint is applied from the very first step.** The blueprints were reverse-engineered from 50 golden clips; each one implies a proven script. So we write the VO in that script's shape from the start — the voiceover is blueprint-shaped before Step 4 ever runs, which makes the blueprint's hit-rate downstream high.
This is still a SOFT discipline. Story truth comes first: **never invent, bend, drop, or reorder a beat to fit a blueprint.** The script patterns only shape HOW a beat is said and which proven shape it leans toward — they never decide which beats exist.
## Read first
Read these inputs before writing:
1. `hyperframes.json` — locked brief: angle, length, aspect ratio, language.
2. `frame.md` — tone, mood, design system, and brand register.
2. `frame.md` — tone, mood, design system, brand register.
3. `capture/extracted/visible-text.txt` — product facts, page copy, positioning, proof, CTA.
4. `capture/extracted/asset-descriptions.md` — the only source for captured asset inventory.
4. `capture/extracted/asset-descriptions.md` — the ONLY source for the captured asset inventory.
5. `user_script.txt` and `VO_MODE`, when present.
Do not inspect `capture/assets/`, contact sheets, screenshots, or raw captured files during Step 3. Treat `asset-descriptions.md` as the canonical asset list. Do not invent asset filenames.
Do not inspect `capture/assets/`, contact sheets, screenshots, or raw files in Step 3. Treat `asset-descriptions.md` as the canonical asset list. Never invent asset filenames.
## Output
Create two files:
- `STORYBOARD.md` — the narrative plan, one frame per beat.
- `SCRIPT.md` — the locked narration, only for spoken frames.
Every storyboard frame must include the required fields from the storyboard format reference, plus the narrative metadata below.
## Core rule
A website is an information layout. A video is an emotional sequence.
Do not follow page order. Reorder, merge, omit, and reshape captured content into a clear launch story.
## Step 3 method
## Method
### 1. Extract the product truth
From the brief and captured text, identify:
From the brief and captured text, name:
- Audience — who the video is speaking to.
- Pain or desire — what the viewer already wants fixed or achieved.
- Promise — the one-line thesis of the video.
- Product role — what the product does in the story.
- Proof — features, UI moments, metrics, logos, examples, or demos.
- CTA — what the viewer should do next.
- **Audience** — who the video speaks to.
- **Pain / desire** — what they already want fixed or achieved.
- **Promise** — the one-line thesis of the whole video.
- **Product role** — what the product does in the story.
- **Proof** — features, UI moments, metrics, logos, demos.
- **CTA** — what the viewer should do next.
Write the storyboard around the promise, not around a list of features.
Build the sequence around the **promise**, not a feature list. A website is an information layout; a video is an emotional sequence. Reorder, merge, and omit captured content freely — do not follow page order.
### 2. Match the register to `frame.md`
### 2. Choose the arc (the sequence backbone)
Use `frame.md` as a soft guide:
Pick ONE arc — it fixes the beat order. Compound only when useful (e.g. `PAS with feature-benefit progression`).
| `frame.md` signal | Story effect |
| ----------------------------- | ----------------------------------------- |
| restrained, editorial, B2B | plain, confident, low-hype script |
| bold, kinetic, launch-like | short hooks, punchier beats |
| warm, human, playful | friendly direct address, lighter phrasing |
| premium, cinematic, spectacle | aspirational framing, fewer words |
| Arc | Use when | Beat order |
| ------------------------- | ------------------------------------------------ | --------------------------------------------------------------------------- |
| `PAS` | Pain is known and urgent (broken B2B workflows). | hook → pain → agitation → solution tease → product intro → proof/demo → CTA |
| `Future Pacing` | Sells a new future / category / paradigm. | imagine → name product → remove pain → mechanism → outcome → CTA |
| `Demo Loop` | UI is self-explanatory; best shown working. | question → product intro → demo cycle 1 → demo cycle 2 → trust → CTA |
| `BAB` | Bridges an old workflow to a better one. | before → after tease → bridge/product → step 1 → step 2 → wow → CTA |
| `Feature-Benefit Cascade` | Feature-rich or desire/status-driven. | category hook → feature → benefit → feature → benefit → climax → CTA |
The product truth decides the arc. The visual system tunes the voice.
Use feature→benefit rhythm inside any arc when there are many capabilities — always translate a feature into viewer value, never stack raw features.
### 3. Choose one outer arc
`frame.md` tunes the VOICE, not the arc: restrained/B2B → plain, low-hype; bold/launch → short, punchy; warm/human → friendly direct address; premium/cinematic → aspirational, fewer words.
Pick one primary arc. Use a compound only when useful, e.g. `PAS with feature-benefit progression`.
### 3. Lay out the beats, each with a role
| Arc | Use when | Beat order | Reveal timing |
| ------------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------------- |
| `PAS` | The pain is known and urgent. Best for broken B2B workflows. | hook → pain → agitation → solution tease → product intro → proof/demo → CTA | late |
| `Future Pacing` | The product sells a new future, category, or paradigm. | imagine → name product → remove pain → show mechanism → show outcome → CTA | early |
| `Demo Loop` | The UI is self-explanatory and the product is best shown working. | question → product intro → demo cycle 1 → demo cycle 2 → trust/benefit → CTA | early |
| `BAB` | The product bridges an old workflow to a better one. | before → after tease → bridge/product → step 1 → step 2 → step 3/wow → CTA | early-mid |
| `Feature-Benefit Cascade` | The product is feature-rich or desire/status-driven. | product/category hook → feature → benefit → feature → benefit → climax → CTA | frame 1 |
Use feature-benefit rhythm inside any arc when the video has many capabilities. Do not stack several features without translating them into viewer value.
### 4. Build the frame sequence
Each frame needs one clear job. Avoid frames that only say “more benefits” or “another feature.”
Use these frame types:
One clear job per beat — never "more benefits" or "another feature." Beat `type` (= blueprint **role**):
`hook | pain_point | product_intro | feature_showcase | benefit_highlight | social_proof | branding | cta`
For every frame, define:
The opening 35s needs ONE hook that creates tension, curiosity, or desire — a shocking stat, pain validation, a rhetorical question, direct address, an imagine/future-pace, a category announcement, or visual spectacle. Never open with generic company description.
- `type` — one of the allowed frame types.
- `persuasion` — the specific persuasion move.
- `beat` — the viewer emotion.
- `scene` — a one-line visual idea, not detailed composition.
- `voiceover` — spoken guide text, or empty for silent frames.
- `asset_candidates` — real captured assets that could support the frame.
A UI demo is usually a SEQUENCE of 3+ consecutive `feature_showcase` / `benefit_highlight` beats on the same surface (input → response → result → benefit), not one isolated frame.
The prose under each frame should state:
### 4. Write each beat's VO in its blueprint's script shape
- `narrativeRole` — why this frame exists in the story.
- `keyMessage` — the one idea the viewer should remember.
For each beat, look up its **role** in the script bank below, find the blueprint whose SHAPE fits the beat you already chose, and **draft the voiceover to sound like that blueprint's pattern.** Tag the candidate `blueprint:` id on the frame.
## Hook strategy
- The bank is the heart of this step: proven product-launch clips reversed into the one VO line each implies, grouped by role → blueprint, each with a **pattern** to imitate.
- If two blueprints fit the beat, prefer the one whose script shape matches the line you'd naturally write.
- If NO shape fits the beat, omit `blueprint:` and write the VO plainly — Step 4 composes that frame freely. Do not force a wrong shape.
- **Vary the shapes across the video.** Reaching for the same blueprint every beat re-creates the sameness this exists to avoid. `kinetic-type-beats` is the workhorse (6 roles) — lean on it, but not everywhere.
- **Write each VO as discrete cues, not one run-on breath.** Step 5 reveals each on-screen piece _when the voiceover names it_ (the anti-PowerPoint mechanism — `motion-language.md` Part 2). A line with clear phrase boundaries — "Content, sentiment, engagement — in one place" — hands the shot its reveal cadence for free; a single long clause leaves the frame nothing to pace to. The bank patterns are already cue-segmented — keep that rhythm.
Pick one opening strategy for the first 3-5 seconds:
Step 3 only TAGS the candidate id and writes the shaped VO. Step 4 (visual design) picks and instantiates the blueprint into a time-coded shot; it may override or drop a Step 3 candidate. The full menu with picking guidance lives in `../hyperframes-animation/blueprints-index.md`.
| Strategy | Use when |
| ----------------------- | ----------------------------------------------- |
| Shocking statistic | A credible number proves the pain. |
| Pain validation | The audience already knows the problem. |
| Rhetorical question | You need curiosity and speed. |
| Direct address | The audience is clear and specific. |
| Imagine / future pacing | The product introduces a new future. |
| Category announcement | The product is the category or launch headline. |
| Visual spectacle | The aesthetic is part of the pitch. |
| Trend positioning | The product rides a timely market shift. |
---
The hook must create tension, curiosity, or desire. Do not open with generic company description.
## The script bank — what each beat's VO sounds like
## Persuasion labels
> Proven product-launch clips, each reversed into the one spoken line it implies. Grouped by **role → blueprint**. Real product names kept (swap in your own). Draft your beat's VO in the SHAPE of the matching pattern.
Use concrete labels. Do not write generic labels like “show benefit.”
### HOOK
Good labels include:
**kinetic-type-beats** — the words ARE the motion
- Pain agitation
- Cognitive overload
- Negative contrast
- Friction reduction
- Simplification
- Show-dont-tell proof
- Demonstration of capability
- Feature-to-benefit translation
- Statistical proof
- Authority by association
- Social proof
- Risk reversal
- Future pacing
- Value stacking
- Empowerment and control
- Rule of three
- Scarcity / urgency
- Status seeking
- Mailoji — "Still using a @gmail address? Or @outlook, or @hotmail, or @yahoo?"
- Outrank — "Getting traffic is hard. Insanely hard."
- AiAgent — "An AI agent that's easy to use — and optimised for you."
- Uizard — "Transform your sketches into prototypes — automatically."
- _Pattern:_ a punchy claim or rhetorical jab whose KEY WORD swaps in place (or escalates beat by beat) — the swap/escalation is the joke.
When no label fits, create one and explain the mechanism in the frame prose.
**typewriter-reveal** — someone is typing this
## Emotional beats
- "Need answers about your audience — right now?"
- Contra — "You are more than your job title. You are more than your resume."
- _Pattern:_ a relatable line typed live and edited mid-stream (a word backspaces and retypes) — the everyday thought, in your own words.
Use specific emotions:
**spatial-pan-stations** — a panned timeline
- Negative: `anxiety`, `frustration`, `overwhelm`, `tension`, `urgency`, `skepticism`, `FOMO`
- Pivot: `relief`, `curiosity`, `clarity`, `intrigue`, `aspiration`
- Build: `trust`, `confidence`, `control`, `ease`, `power`, `awe`, `excitement`, `belonging`, `reassurance`
- Close: `triumph`, `motivation`, `urgency-to-act`, `peace of mind`, `inevitability`
- Rows — "From VisiCalc to Excel to Google Sheets — the spreadsheet has barely changed since 1979."
- _Pattern:_ a march of named milestones across time, landing on "...until now / ...up to us."
Compound beats are allowed, e.g. `relief + control`.
**constellation-hub** — nodes ring a center, camera pushes in
- "Content, sentiment, engagement, analysis — every platform you're on, in one place."
- _Pattern:_ a spread of tools/channels collapsing onto one center — "it connects everything."
**ticker-takeover** — options cycle, then a hero crashes in
- Notion — "A doc? A database? A wiki? — no, it's all of them, in one place."
- _Pattern:_ a "could be X, or Y, or Z?" cycle on one swapping word, then a hero claim crashes in and replaces it — "actually, this is what it is."
### PROBLEM
**kinetic-type-beats** — pain lands alone on a bare canvas
- Butter — "What if your sessions didn't have to be boring and unstructured — or buried under a dozen tabs?"
- SmartCue — "You asked for better leads. We were the cure — MQLs that actually convert, a sales team that becomes your ally."
- _Pattern:_ 35 short pain statements (or a "what if?" framing), each landing solo before the next — no product yet.
**spatial-pan-stations** — a panned web of pain
- Vauban — "Coordinating legal documents, signatures, and cross-border transactions — it's a tangled mess."
- _Pattern:_ pain "stations" traversed one by one, ending on a knot — "too many disconnected steps."
**dataviz-countup** — the data IS the argument
- "67% of professionals say leadership is disconnected — and it's costing them a 65% boost in profitability."
- _Pattern:_ a count-up / chart / stat the camera pushes through to dramatize a worsening or large-scale problem.
**overwhelm-surround** — buried by your own tools
- "Slack, email, docs, tickets, three more tabs — and somehow it all lands on you."
- _Pattern:_ recognizable tools pile in until they surround and bury the viewer — the pain is being swamped, not one bad number.
### PRODUCT_INTRO
**kinetic-type-beats** — "Introducing…" name-drop
- "Elevating experiences, removing manual touchpoints, automating processes — so you can focus on the customer."
- Uizard — "Introducing Uizard — the design tool for everyone."
- _Pattern:_ hard-cut through "Introducing…" / tagline / value beats and resolve on the brand name or logo.
**logo-assemble-lockup** — wordless premium sting
- Manifold — "Manifold." _(wordless mark assembles; VO optional — just the name)_
- _Pattern:_ an abstract system assembles around a fixed mark — no copy, or just the product name landing.
**cursor-ui-demo** — first cursor-led look
- ClickUp — "This is ClickUp — click through and watch your whole workspace change."
- "Pull up any contact, find the right advisor, and you're matched in seconds."
- PaLM 2 — "Meet PaLM 2 — what is it, what can it do, and how was it built?"
- _Pattern:_ a cursor sweeps in to introduce the surface — a light first look landing on a hovered hero element or fresh result.
**dataviz-countup** — open on the result
- SuperX — "X growth — discover what really works: 19.6 million impressions."
- _Pattern:_ a confident "look at the data / the result" open — scroll a tilted card grid to one glowing hero metric, tagline assembling word by word.
**video-text-pivot** — show it work, then the number
- "Watch it run — then look at what it saved: 14 hours, every week."
- _Pattern:_ the product video plays, then slides aside to hand the frame to one impact stat — "see it work, now see what it's worth."
### KEY_FEATURE
**grid-card-assemble** — enumerate breadth at once
- Postcards — "Want more? Unlimited exports, 1,400 fonts, an AI assistant, version history — and it's free to try."
- ClearVPN — "ClearVPN is built to help you: streaming access, secure browsing, location changing — only the essentials."
- Copilot — "Command bar, Zapier, white-labeling, API, SOC2 — and a whole lot more."
- _Pattern:_ a tile/pill/card grid self-assembles to show many capabilities at once — "look how much it does."
**cursor-ui-demo** — one workflow, end to end
- "Scroll your feed, then jump straight to your notifications — it's all one click away."
- Flowrite — "Pick your recipient, set your intention, choose a tone — and Flowrite writes it for you."
- Descript — "Tune your edits, add a crossfade, automate the volume, normalize loudness — then export."
- _Pattern:_ one specific multi-step workflow shown end-to-end across 24 real edits, landing on the action button or result.
**device-surface-showcase** — experienced inside its real interface
- HRS — "Your flight's cancelled — so book a hotel, a taxi, and get reimbursed, all in one digital journey."
- HelpKit — "A dynamic table of contents that follows your readers as they scroll."
- Graphite — "Use unique themes, recolor one element or several, and configure it all in a handy window."
- Contra — "Pick a template, make it yours, and launch a portfolio that's unmistakably you."
- _Pattern:_ a feature shown being USED inside its real surface — the device/window is the hero and its screens advance through a flow.
**comparison-split** — two paired capabilities, side by side
- "Design on the left, code on the right — always one source of truth."
- _Pattern:_ two complementary capabilities of equal weight shown together — "X and Y, in lockstep."
**video-text-pivot** — the feature, then its result
- "Here's the editor in action — and the result: a publish-ready cut in minutes."
- _Pattern:_ a feature clip runs, then yields the frame to a metric / impact line — the clip proves it, the number lands it.
### BENEFITS
**kinetic-type-beats** — rapid-fire value montage
- AiAgent — "No API keys, GPT-4 access, simple UX, clean UI — moving fast."
- Uizard — "Export to Sketch, create style guides, share, collaborate — and code less."
- _Pattern:_ a staccato montage of 812 short value phrases, each flashing and clearing at high tempo.
**grid-card-assemble** — an accumulating value list
- Lineicons — "Consistent and clean, tons of free icons, a Figma plugin, a powerful editor, every format you need."
- Plutio — "Manage projects, track time, send invoices, write proposals — all deeply customisable."
- _Pattern:_ short value phrases populate a vertical list ~1/sec, co-resident and accumulating, each popping into its slot.
**titlecard-reveal** — the calm value beat
- CSS Scan Pro — "A smart color picker — with instant tints and shades."
- _Pattern:_ one clean two-line value title, one slide-up crossfade, then held still. Low motion is the point.
### SOCIAL_PROOF
**constellation-hub** — the hub at the center of your stack
- kyvos — "On any BI tool — Tableau, Looker, Power BI — Kyvos sits at the center of your stack."
- _Pattern:_ the product mark is the hub and partner logos orbit it — "sits at the center of everything you use."
**grid-card-assemble** — a logo wall pulling back to a vast ecosystem
- Lineicons — "Used by more than 100,000 designers, developers, and companies — including these."
- ClickUp — "Connect Google Drive, Slack, GitHub, Stripe — your whole ecosystem in one place."
- Copilot — "With thousands of partner apps — Airtable, Calendly, Jira, Asana — you can embed anything."
- _Pattern:_ a wall of partner/app logos builds, then a camera zoom-out reveals a vast ecosystem.
**titlecard-reveal** — busy → clean proof card
- Trumpet — "We supply the trumpet, you bring the band — loved by 1,000+ sales, success, and marketing teams."
- _Pattern:_ wipe a busy open away to a clean lockup plus a "loved by N+ teams" line that settles and holds.
### CTA
**kinetic-type-beats** — punchy closing line beat-by-beat
- Stylebit — "Go pro, connect up to five Figma accounts — more features coming. Join now."
- revid.ai — "Boost your engagement and turbocharge your social media."
- _Pattern:_ a closing line (or short value stack) that snaps in beat by beat and lands on the logo or URL.
**logo-assemble-lockup** — logo build → push-through into the URL
- Strapi Cloud — "Deploy on Strapi Cloud — no server hassle, same flexibility. Try it now at strapi.io/cloud."
- Highlander — "Highlander is ready for the future you're building. Let's raise — at highlander.ai."
- STUDIO AI — "Get early access to STUDIO."
- Glorify — "Get started for free now — no credit card required."
- _Pattern:_ the logo builds, a fast camera push-through streaks giant CTA letters past the lens, resolving on the URL or action verb.
**cta-morph-press** — identity condenses into one click
- Linear — "This is Linear. Start building — it's free."
- _Pattern:_ the brand mark condenses straight into the single thing you click — "here's us → click here," no spatial set.
### BRAND_OUTRO
**kinetic-type-beats** — a verb barrage resolving on one word
- Phantom — "Buy, store, stake, swap, send, connect, explore — multichain."
- _Pattern:_ a rapid center-channel barrage of single-word verbs asserting breadth, resolving on the brand's one defining word. No logo lockup needed.
**typewriter-reveal** — persistent mark + typed CTA rail
- Collato — "Next time, just Collato it — sign up for free today."
- _Pattern:_ the mark holds dead-center the whole time while a sub-line types or swaps into the final CTA.
**logo-assemble-lockup** — elements clear, the lockup draws itself in
- Copilot — "Copilot." _(pills disperse off-frame, the mark draws on; VO optional — just the name)_
- Dora AI — "Dora AI — join the waitlist."
- _Pattern:_ feature/UI elements clear the stage off all four edges, then the logo mark draws itself on and the wordmark completes the lockup.
**ticker-takeover** — use-cases cycle, the brand takes the frame
- "For notes, for tasks, for plans, for teams — [brand] holds it all."
- _Pattern:_ closing use-cases/verbs cycle through one slot, then the brand mark crashes in and owns the final frame.
---
## VO_MODE handling
**No pasted script** — write the VO yourself, in the matching blueprint's script shape:
- 12 sentences per spoken beat, usually 620 words.
- Concrete and human; active verbs; say what the product does for a person.
- Avoid: "seamless experience," "unlock the power of," "streamline your workflow," long noun-phrase lists, a whole beat that is just "Or…".
- Silent beats are allowed when the visual proves the point — leave them out of `SCRIPT.md`.
**`VO_MODE = restructure`** — treat `user_script.txt` as source material. Rewrite, reorder, merge, or omit to fit the arc and target length. You may still shape each segment toward its beat's blueprint pattern.
**`VO_MODE = verbatim`** — do NOT change the user's words. Segment the script into beat-sized chunks at sentence/paragraph boundaries (split a long sentence only at a natural clause boundary). Final duration follows the provided script. Blueprint shaping does not apply to wording — only to which shape each verbatim chunk is paired with.
## Asset candidates
`asset_candidates` is the handoff from Step 3 to visual design and frame building.
`asset_candidates` is the Step-3 → Step-4 handoff. Rules:
Rules:
1. Read only `capture/extracted/asset-descriptions.md` to know what assets exist.
2. Use only filenames listed there.
3. Write candidates as `assets/<basename>`, where `<basename>` is the captured file basename.
4. Put all candidates on one line.
5. Separate candidates with semicolons.
6. Add a short description after `—`.
7. Prefer `[video]` assets when motion proves the product better than a still.
8. Use content assets: UI, screenshots, product photos, charts, diagrams, demos.
9. Skip tiny icons, favicons, badges, decorative chrome, and repeated logo variants unless the frame needs them.
10. Pure typography frames may use an empty asset list.
1. Read only `capture/extracted/asset-descriptions.md` to know what exists.
2. Use only filenames listed there; write as `assets/<basename>`.
3. One line, candidates separated by semicolons, a short description after `—`.
4. Prefer `[video]` assets when motion proves the product better than a still.
5. Use content assets (UI, screenshots, product photos, charts, demos). Skip tiny icons, favicons, badges, decorative chrome, repeated logo variants — unless the beat needs them.
6. Pure-typography beats may use an empty asset list. Do not use nested lists.
Example:
```md
- asset_candidates: assets/dashboard-hero.png — main analytics UI, dark dashboard, wide screenshot; assets/demo-loop.mp4 — product interaction clip, query to result flow
- asset_candidates: assets/dashboard-hero.png — dark analytics dashboard, wide screenshot; assets/demo-loop.mp4 — query-to-result interaction clip
```
Do not use nested lists for assets. The parser expects a single metadata line.
## transition_in
## UI demos
Between-frame transition — how each frame ENTERS from the one before it. The harness's injector stamps it onto the two whole-frame clips (opacity / transform / filter on the frame wrappers). Name a **registry type** directly; optionally add a direction and/or a duration (`push-slide LEFT`, `crossfade 0.4s`). `cut` / `none` / empty = a hard cut.
A UI demo should usually be a sequence, not one isolated frame.
The five registry types:
Use 3 or more consecutive `feature_showcase` / `benefit_highlight` frames on the same product surface when the product value depends on workflow or interaction.
- **`crossfade`** — a plain opacity dissolve; the neutral choice when two frames sit in the same visual world.
- **`blur-crossfade`** — dissolve through a soft blur + slight scale; use when the two frames' backgrounds differ a lot, so the blur masks the color clash a plain crossfade would expose.
- **`push-slide`** `[LEFT|RIGHT|UP|DOWN]` — outgoing slides off, incoming pushes in from the opposite edge; a lateral "next beat" feel for a run of consecutive cards / feature beats.
- **`zoom-through`** — outgoing scales up + blurs out, incoming scales up from small into focus; for a STATE CHANGE / turning to a new section (hook → context).
- **`squeeze`** — outgoing compresses to a line on one edge as incoming expands from the other; a snappy, mechanical beat change.
Good demo rhythm:
1. Input or user action.
2. Product response.
3. Result, insight, automation, or saved effort.
4. Benefit or trust proof.
Use consistent transitions across the sequence so it feels like one flow.
## Script rules
### If there is no pasted script
Write tight per-frame narration:
- 1-2 sentences per spoken frame.
- Usually 6-20 words per frame.
- Concrete and human.
- Say what the product does for a person.
- Prefer active verbs.
Avoid:
- “Seamless experience.”
- “Unlock the power of...”
- “Streamline your workflow.”
- Long noun-phrase lists.
- A whole frame with only “Or...” or another filler bridge.
Silent frames are allowed when the visual proves the point. Leave them out of `SCRIPT.md`.
### If `VO_MODE = restructure`
Treat `user_script.txt` as source material. Rewrite, reorder, merge, or omit to fit the chosen arc and target length.
### If `VO_MODE = verbatim`
Do not rewrite the users words. Segment the script into frame-sized chunks at sentence or paragraph boundaries. You may split a long sentence at a natural clause boundary, but do not change words. The final duration follows the provided script.
## Transitions
Use only registry transition names:
`cut | crossfade | blur-crossfade | push-slide LEFT | push-slide RIGHT | push-slide UP | push-slide DOWN | zoom-through | squeeze`
Pick 2-3 transition types for the whole video and repeat them. Frame 1 can use `cut` as a placeholder.
Pick a small set and repeat them: default to `crossfade` (or `blur-crossfade` when the backgrounds clash), and reach for `zoom-through` at section boundaries. Frame 1's `transition_in` is a placeholder.
## Frame template
Use the exact fields required by the core storyboard format. This is the narrative shape each frame should satisfy:
Use the exact fields required by the core storyboard format. The narrative shape each frame satisfies:
```md
## Frame N — Short name
- scene: one clear visual idea
- voiceover: "spoken guide text, or empty"
- voiceover: "spoken line, written in the candidate blueprint's script shape, or empty"
- duration: rough estimate in seconds
- transition_in: cut
- transition_in: crossfade
- status: outline
- src: compositions/frames/NN-short-name.html
- type: hook
- persuasion: Pain validation
- beat: urgency
- blueprint: kinetic-type-beats — candidate shape from the role→blueprint menu; omit when none fits
- asset_candidates: assets/example.png — short asset description
narrativeRole: What this frame does in the viewer journey.
keyMessage: The one idea the viewer should remember.
narrativeRole: what this beat does in the viewer journey.
keyMessage: the one idea the viewer should remember.
```
- `persuasion` — a concrete move (Pain agitation, Negative contrast, Friction reduction, Show-don't-tell proof, Feature-to-benefit translation, Statistical proof, Authority by association, Social proof, Risk reversal, Future pacing, Value stacking, Rule of three, Scarcity/urgency, Status seeking…). Never "show benefit." Invent one if none fits and explain it in the prose.
- `beat` — a specific emotion (anxiety, frustration, overwhelm, tension, urgency, skepticism, FOMO → relief, curiosity, clarity, intrigue, aspiration → trust, confidence, control, ease, power, awe, excitement, belonging → triumph, motivation, urgency-to-act, peace of mind, inevitability). Compound allowed (e.g. `relief + control`).
## Final checklist
Before asking for user approval, verify:
- The arc is named and consistent.
- The sequence is narrative-driven, not page-order-driven.
- The opening uses a clear hook strategy.
- Each frame has one job.
- Every frame has `type`, `persuasion`, and `beat`.
- Every visual frame has suitable `asset_candidates`, unless intentionally typography-only.
- Asset filenames come only from `capture/extracted/asset-descriptions.md`.
- UI/product demos use a multi-frame sequence when needed.
- Transitions use only registry names and repeat 2-3 types.
- `SCRIPT.md` contains only locked spoken narration.
- Silent frames are intentional and omitted from `SCRIPT.md`.
- The arc is named; the sequence is narrative-driven, not page-order-driven.
- The opening uses one clear hook strategy that creates tension/curiosity/desire.
- Each beat has one job; every beat has `type`, `persuasion`, `beat`.
- Each beat's `voiceover` is written in its candidate blueprint's script shape (from the bank), with the candidate `blueprint:` tagged wherever a shape fits — and omitted where none does.
- Each `voiceover` is phrase-segmented into cues (each cue a piece Step 5 can reveal on) — not one long run-on clause.
- Shapes vary across the video; no single blueprint on every beat.
- Story truth was never bent to fit a blueprint — no beat invented/dropped/reordered for a shape.
- Every visual beat has suitable `asset_candidates` (filenames only from `asset-descriptions.md`), unless intentionally typography-only.
- UI/product demos use a multi-beat sequence when the value depends on workflow.
- `transition_in` is a registry type (`crossfade` / `blur-crossfade` / `push-slide` / `zoom-through` / `squeeze`) — default `crossfade` (`blur-crossfade` on a background clash), `zoom-through` at section boundaries, repeated across the video.
- `SCRIPT.md` contains only locked spoken narration; silent beats are intentional and omitted.
@@ -1,55 +1,94 @@
# Visual design — product-launch per-frame enrichment method
# Visual design — product-launch per-frame shot method
> The method behind **Step 4 (Frame visual design)**. You (the orchestrator) read it to **enrich `STORYBOARD.md` frames in place** — story-design wrote the skeleton (each frame's `scene`, `voiceover`, `transition_in`, the five narrative fields, and its `asset_candidates`); you add how each frame **looks and moves**. Each frame is a **directed shot, not a static slide** — you choreograph it across its whole duration, not just its entrance. You write **no HTML** (that's the frame workers), you **never read `capture/`** (story already chose the assets), and you do **not** select assets or name transitions (story owns both). `frame.md` is your palette/type truth. Composition / motion detail lives in `composition.md` + `motion-language.md`; effect & blueprint **bodies** live in `hyperframes-animation`. Adding palette theory or a generic font rule here? Wrong home — `frame.md` + `hyperframes-creative`.
> The method behind **Step 4 (Frame visual design)**. You (the orchestrator) read it to **enrich `STORYBOARD.md` frames in place** — story-design wrote the skeleton (each frame's `scene`, `voiceover`, `transition_in`, the narrative fields, its `asset_candidates`, and optionally a candidate blueprint id); you add how each frame **looks and moves**. The unit you write per frame is a **time-coded shot sequence** — a shot directed across its whole duration, not a static slide. You write **no HTML** (that's the frame workers), you **never read `capture/`** (story already chose the assets), and you do **not** select assets or name transitions (story owns both). `frame.md` is your palette/type truth by role. Layout is a compact vocabulary in this file (the **Layout** section below), stated inline per Scene; motion vocabulary + the motion doctrine + the seek-safe core → `motion-language.md`; the proven shapes → `../hyperframes-animation/blueprints-index.md` + `blueprints/<id>.md`; concrete rules resolve in Step 5 from this skill's local `../hyperframes-animation/rules/`. Adding palette theory or a generic font rule here? Wrong home — `frame.md` + `hyperframes-creative`.
## Every frame is a directed shot
## The unit is a time-coded shot sequence
A frame's visual layer is choreographed across its **full duration**, not front-loaded into an entrance. The failure that reads as PowerPoint: content animates in over the first ~0.8s, then **freezes** while a slow drift plays under it. So every frame's metadata + note describe a **shot with phases**`entrance → development → settle` — where _development_ (a reveal, a rearrange, a morph, an emphasis hit, a count-up) is the mid-shot motion that separates video from slides. The shot model and the choreography-vs-idle budget live in `motion-language.md`; here you **encode it into the frame**: the `effects` / `blueprint` ids are the motion vocabulary, and the **composition note sequences them into phases**.
A frame's visual layer is **a sequence of time windows paced to the voiceover**, not a bag of effect tags. The failure that reads as PowerPoint is **front-loading**: the agent rushes the whole canvas on screen in the first ~25%, and then it just sits (the old representation — a flat set of effect names + a prose note — fired everything at entrance and left the rest empty). A time-coded shot sequence written **against the VO** makes that impossible: each window states what is on screen and what is moving, and **nothing appears before the voiceover reaches it.**
Deliberate **stillness** is the marked exception — the 2-3 climax/breather frames you allocate in `## Video direction`. Every other frame develops; a held frame outside that allocation is just a slide.
Write each frame as a handful of windows cued by the spoken line:
```
Scene 1 (0.0Xs): only what the VO is saying at t=0 enters — never the whole canvas
Scene 2 (XsYs): the next piece reveals as the VO names it (a line / card / stat / icon)
… one window per spoken cue — as many or as few as the line calls for
Scene N (…–end): content has resolved; hold the read (stillness; subtle jitter at most)
```
- Each `Scene` line names **what's on screen**, **what moves in this window**, and **where it sits** (layout, inline). Times are real seconds across the frame's `duration`.
- **Pace reveals to the voiceover; never front-load.** This is the core anti-PowerPoint mechanism (→ `motion-language.md` Part 2 Rule 2). At t=0 show only what the VO is saying then; reveal each further piece — a line, a card, even an h1 — **when the VO names it**, spreading reveals across the shot and especially the **back ~50%**. **The window count = the number of spoken cues the line calls for** — a two-beat line is two windows, a five-feature list is five or six. There is **no fixed count and no mandatory "middle" act**; the only sin is dumping everything up front.
- **End on a held read.** Once the content has resolved it holds and reads — **prefer stillness to bad motion**: no forced camera drift, no lazy breathing, no back-half pan/push; at most a subtle jitter keeps it alive (→ `motion-language.md`). On a short shot the final reveal and the hold are the **same window** — the hold is not a separate mandatory act. Only the final frame has a real exit; every other frame's exit is the harness transition (story's `transition_in`).
- A **deliberately held** frame — content already revealed, now reading still — is legitimate and often right (a climax, a breather). The failure is never "too still"; it is **front-loaded-then-frozen** (everything dumped by ~25%, nothing cued to the VO). Place held beats deliberately for rhythm so the video isn't uniformly busy (allocate them in `## Video direction`). Reveal pacing + holds + the idle budget → `motion-language.md`.
## Pick the shape — instantiate a blueprint
Don't invent each shot from scratch. The frame's **role** (its `type` / `beat`) points to a proven shape:
1. **Match the role to a blueprint.** Open `../hyperframes-animation/blueprints-index.md`, find the frame's role in the **role→blueprint menu**, and pick the blueprint whose intent fits this beat (story may already have named a candidate id — confirm or override it). Read that `blueprints/<id>.md`: it is a short, product-agnostic, **time-coded shot template with `[slots]`** and a named **signature move** (the thing that makes the shape itself — the SVG ring, the push-THROUGH, the in-place token swap).
2. **Instantiate its `[slots]` with THIS product's content** — three postures:
- **Reproduce** — the blueprint fits the beat and your content maps onto its slots cleanly. Fill every `[slot]` with this product's word / asset / stat and follow its Scene timing. Write the resulting Scene lines.
- **Adapt** — the _structure_ fits but the content / asset-count / surface doesn't (or you want a fresher surface to avoid templating). State **what you keep / what you change** in one line, then write the adapted Scene lines. You may extend or vary; you may **never** drop the **signature move** (drop it and you picked the wrong blueprint), and you keep the reveals **paced to the VO** — never collapse the shape to a single front-loaded dump.
- **Compose** — no blueprint fits the beat. Build the shot from the **motion vocabulary** in `motion-language.md`: still pace the reveals to the VO across the shot, never fire everything at t=0. Mark it `blueprint: compose`.
3. **Keep the signature move.** Whichever posture, the blueprint's signature move (named in its file) is the spine of the shot — it usually lands on the shot's key reveal. Carry it through.
The blueprint's own Scene lines, motion vocabulary, and `rule mapping` are your raw material; you are choosing a shape and casting this product into it, not copying an engineering spec.
## What you add to each frame
Story-design's `## Frame N` block already carries the narrative. You append the visual layer as frame metadata + one composition note (story's role/message prose stays):
Story-design's `## Frame N` block already carries the narrative + `asset_candidates`. You append the shot. Story's `scene` / `voiceover` / `transition_in` / role fields stay untouched.
```
## Frame 3 — The problem
- scene: a 20-minute timer spins on a stack of rejected takes ← refine only if it could read sharper
- scene: a 20-minute timer over a stack of rejected takes ← refine only if it could read sharper
- voiceover: "…" ← story's; leave it
- transition_in: crossfade ← story's; leave it
- type: pain_point ← story's
- persuasion: Pain agitation
- beat: frustration
- effects: slow-push, count-up, vignette-pulse ← you add: cite effect ids (≥3, sequenced into the phases below)
- blueprint: messaging-multi-phase ← you add (optional): one multi-phase blueprint id
- focal: assets/timer-stack.png ← you add: which existing candidate is the hero
- roles: timer-stack = background (dim ~40%) ← you add: cutout / background / supporting per candidate
- sfx: impact-soft, riser ← you add: the sound the beat wants (fetched + mounted at root; never yours to embed)
- blueprint: dataviz-countup (Adapt) ← you add: the id you instantiated (or "compose")
- focal: assets/reject-stat.png ← you add: the hero asset for this beat
- roles: reject-stat = cutout · timer = supporting · backdrop = background (dim ~40%) ← you add: role per candidate
- sfx: impact-soft, riser ← you add: the sound the beat wants (fetched + mounted at root; never yours to embed)
Entrance: timer drops in upper-left (heavy), rejected-takes stack seated low. Development: the stack grows beat-by-beat toward the band as the reject count ticks up (count-up). Settle: vignette pulses in and holds; only the slow-push continues. A dense, edge-anchored frame.
Adapt: keep the count-up-ring signature; one stat not three, and the trend chart becomes the rejected-takes count climbing.
Scene 1 (0.01.2s): solid backdrop (dim ~40%); a circular progress ring + bold center number seat dead-center, ring sweeps and number counts 0→20 on one heavy ease — Centered template, ~50% of frame. Slow push-in runs underneath.
Scene 2 (1.23.4s): as the VO names the count, the camera pushes THROUGH the ring into the rejected-takes stack lower-left; the stack grows beat-by-beat as a reject counter ticks up beside it (the count-up reveals on its spoken cue, not at t=0). Asymmetric 60/40, 3 depth layers.
Scene 3 (3.45.0s): land the hero stat card dead-center, accent glow blooms behind it and holds; the stat reads clean and STILL — no continuing push, no breathing (a held beat beats bad motion). The stillness reads against the prior motion.
```
- **`effects`** — name atomic effect **ids** from `hyperframes-animation`'s rules index. **Cite ≥3 when you name no `blueprint`** (the worker composes them into the beat; fewer than 3 reads as generic motion); 1+ as accents when a blueprint already carries the choreography. With no blueprint, those **≥3 effects are the shot's phases** — your note must **sequence them** (one enters, one develops, one emphasizes), not list them as a flat set that all fires at entry (that collapses three phases into one slide). The names are a shared vocabulary; the recipe lives there — you cite, the worker **reads the body and reproduces it** (not a name-guess).
- **`blueprint`** — name **one** multi-phase blueprint id from `hyperframes-animation/blueprints-index.md` when a frame's beat wants a proven multi-phase shape (multi-phase reveal, orbit-collapse, …). Two postures — **both require the worker to read the recipe body (and run its `examples/<id>.html` to see the signature move) first**; deviating from a name-guess instead of from understanding is the one banned failure:
- **Reproduce** — the blueprint fits the beat cleanly and the frame's content maps onto its slots; the worker reproduces its phases faithfully. Write the composition note shot-by-shot to match.
- **Adapt** — the blueprint is the right _structure_ but the content / beat / asset-count doesn't fit its exact form, or you want the proven skeleton with a fresher surface (anti-templating), or you're grafting a second blueprint's phase. Lead the note with a **`Base / Keep / Depart`** line — `Base:` the blueprint id · `Keep:` its **signature** (the move that makes it itself — the SVG ring, the orbit→collapse; never drop this, or you named the wrong blueprint) · `Depart:` what you change and why. Adapt may **extend or vary, never reduce below the shot model** — never flatten a multi-phase blueprint into a single entrance. E.g. `Base: avatar-cloud-network · Keep: the SVG-connected elliptical ring · Depart: 7 logos not 12, + a hub count-up as the development beat`.
The lightweight tags:
Choose **Reproduce** when the shape fits as-is, **Adapt** when the structure fits but the form doesn't (or to avoid templating); **omit** `blueprint` entirely when no blueprint's structure fits — then the cited `effects` (≥3) carry the phases (**Compose**).
- **`blueprint:`** — the id you instantiated (with `(Reproduce)` / `(Adapt)`), or `compose`. One id per frame.
- **`focal:`** — which existing candidate is the hero of this beat.
- **`roles:`** — each candidate's role: `cutout` (foreground subject, lay text around it) · `background` (full-bleed, dim 3050%) · `supporting` (secondary). You **consume** the candidates story chose — never add, swap, or drop one (coverage is story's call; if a frame truly has the wrong candidates, flag it back, don't reach into `capture/`).
- **`sfx:`** — name the sound the beat wants (an impact for a slam, a whoosh for a push, a riser into a reveal). The audio script's `fetch-sfx` pass retrieves it and the assembler mounts it at the root — you only **name** it, never embed an `<audio>` element.
- **`focal` / `roles`** — story listed `asset_candidates`; you pick the **focal** hero and each candidate's role (`cutout` = foreground subject, lay text around it; `background` = full-bleed, dim 30-50%; supporting = secondary). You **consume** the candidates — never add, swap, or drop one (coverage is story's call; if a frame truly has the wrong candidates, flag it back, don't reach into `capture/`).
- **`sfx`** — name the sound the beat wants (an impact for a slam, a whoosh for a push). The audio script's `fetch-sfx` pass retrieves it from HeyGen and the assembler mounts it at the root — you only **name** it, never embed an `<audio>` element.
- **composition note** — the frame's visual brief: layout, hero, depth layers, the macro move, **and the shot's phases**. **Default to a phased note**`entrance: … → development: … → settle: …` (shot-by-shot; mandatory when you named a `blueprint`) — naming what's on screen and what moves in each phase, so the worker builds the development instead of freezing after entry. A **single still line** is correct only for a deliberately held climax or an allocated stillness frame. Full method → `composition.md` (layout) + `motion-language.md` (phases).
**Layout is stated INLINE in each Scene line** — name the template, density, depth, and hierarchy as part of "where it sits" (`Centered, ~50% of frame`, `asymmetric 60/40, 3 depth layers`), drawing on the **Layout** vocabulary below; never write px / scale / shadow recipes (the worker writes those).
## Video direction — write the invariants ONCE
**Motion is named INLINE in each Scene line** — name the move from `motion-language.md`'s vocabulary (`ring sweeps`, `pushes THROUGH`, `count-up`, `glow blooms`) and let it settle on a long-tail curve (`power3` default — smooth beats bouncy; see `motion-language.md`). Never write ease curves / ms / stagger (those resolve in Step 5 from this skill's local `../hyperframes-animation/rules/`).
The whole video shares one look and one motion grammar. State it **once**, at the top of `STORYBOARD.md` (a `## Video direction` block), so every frame inherits it and per-frame metadata carries only the **delta**:
## Layout — named inline per Scene
State each Scene's layout as part of "where it sits." **If the blueprint already implies a composition** (a ring around a center, stations on a wide canvas, two cards from opposite wings), that wins — describe it directly; the vocabulary below is for **composing freely** or a generic beat, not a menu you must pick from. Never write px / scale / shadow (the worker does). One frame's layout can EVOLVE across its Scenes (Scene 1 centered hero → Scene 2 rearranges to a grid).
- **Framing vocabulary** — centered (hero / climax) · rule-of-thirds · split-screen (comparison) · layered-depth (immersive) · asymmetric 60/40 or 70/30 (editorial) · triptych (three panels) · full-width strip. Vary the framing across the video so it doesn't read as one repeated template — let the beat decide, not a quota.
- **Density** — primary visual ≥ 40% of canvas; ≥ 3 depth layers (background + midground + foreground); never a lone small cluster floating in empty space. Squint test: after blur you can still pick out the #1 element.
- **Hierarchy** — combine ≥ 2 of size (3:1) / weight (800 vs 400) / contrast / position (upper-third is golden) / motion, so one element clearly dominates.
- **Depth** — layer 23 of: size, blur, opacity gradient, overlap, shadow-stack.
- **Don't show**: nav bars, footers, scrollbars, real cursors / browser chrome, generic decorative shapes standing in for a real asset, floating bokeh / purple-blue "AI" gradients — unless it's an intentional UI-demo reconstruction.
## `## Video direction` — write the invariants ONCE
The whole video shares one look and one motion grammar. Write a **`## Video direction`** block ONCE at the top of `STORYBOARD.md` so every frame inherits it and per-frame Scene lines carry only the **delta**. This block is load-bearing — it is what binds many independent shots into one film. **Keep it.**
- **palette system** — from `frame.md`: which roles map to which hues. Never invent.
- **motion defaults + shot model** — default eases + the **choreography baseline** (every frame a directed shot: entrance → development → settle) + the **idle-life budget** (what may keep moving during the hold) (→ `motion-language.md`).
- **negative list** — what never appears: off-brand textures and effects the pack forbids, **plus both motion failure modes** — slideshow (enter-then-freeze) and screensaver (everything floating independently) (`motion-language.md`).
- **stillness allocation** — name the 2-3 frames that hold still before a climax; every other frame develops (the anti-repetition discipline; `motion-language.md`).
- **motion grammar + reveal model** — long-tail eases (`power3` default, smooth over bouncy) + the **VO-paced reveal** model every frame follows (reveal each piece on its spoken cue; never front-load) + what may stay alive during a hold (subtle jitter at most; no lazy breathing) (→ `motion-language.md`).
- **rhythm / held-frame allocation** — name the **held / breather frames** (often before a climax) so the video varies its energy: most frames reveal to the VO, a few hold still (a held read beats bad motion; the anti-monotony discipline; `motion-language.md`).
- **negative list** — what never appears: off-brand textures / effects the pack forbids, **plus both motion failure modes** — slideshow (front-load then freeze) and screensaver (everything floating independently) (`motion-language.md`).
Do **not** repeat these in every frame — restating video-level rules per frame is exactly the bloat this layer prevents. Each frame's metadata is the delta on top of Video direction.
Do **not** repeat these per frame — restating video-level rules in every frame is exactly the bloat this layer prevents.
## Palette & type — from `frame.md`, never invented
@@ -58,26 +97,30 @@ Do **not** repeat these in every frame — restating video-level rules per frame
## Caption-band keep-out (plan side)
The bottom ~17% of the canvas is reserved for the caption pill. Plan every frame's content into the **top ~83%** so nothing important lands in the band (the worker enforces the pixel cutoff; you plan the layout). Holds even when captions are disabled — bottom-edge consistency. Geometry detail → `composition.md`.
The bottom ~17% of the canvas is reserved for the caption pill. Plan every frame's content into the **top ~83%** so nothing important lands in the band (the worker enforces the pixel cutoff; you plan the layout). Holds even when captions are disabled — bottom-edge consistency.
## Where the detail lives
| For… | Read |
| --------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| composition — zones, density, templates, asset prominence, caption geometry | `composition.md` (local) |
| motion — the shot model, phases, idle budget, beat structure, stillness | `motion-language.md` (local) |
| effect ids + blueprint ids (vocabulary + recipes) | `../hyperframes-animation/blueprints-index.md` + `../hyperframes-animation/rules-index.md` |
| palette + type tokens | the project's `frame.md`; basics → `hyperframes-creative` `house-style.md` / `typography.md` |
| "produced, not generated" foreground density | `hyperframes-creative/references/video-composition.md` |
| transitions | story-design owns `transition_in`; you don't touch it |
| For… | Read |
| ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| the proven shapes + role→blueprint menu + how to pick | `../hyperframes-animation/blueprints-index.md``blueprints/<id>.md` (local) |
| motion — shot model, vocabulary, holds, idle budget, stillness, seek-safe | `motion-language.md` (local) |
| layout — templates, density, depth, hierarchy, caption band | the **Layout** vocabulary in this file |
| concrete eases / ms / stagger + rule recipe bodies (Step 5) | local `../hyperframes-animation/rules/` (the frame worker reads it; you don't) |
| palette + type tokens | the project's `frame.md`; basics → `hyperframes-creative` `house-style.md` / `typography.md` |
| "produced, not generated" foreground density | `hyperframes-creative/references/video-composition.md` |
| within-frame cuts / seams (zoom-through · cut-the-curve · waterfall) | `cut-catalog.md` (the worker builds them inside the composition) |
| transitions | story-design owns `transition_in`; you don't touch it |
## Before you finish — checklist
- Every frame has `effects` (≥1 cited id; **≥3 when no `blueprint`** is named); a `blueprint` where the frame matches one, with a shot-by-shot composition note.
- **Every frame's composition note is phased** (entrance → development → settle / shot-by-shot) — not a single entry that then freezes; the ≥3 effects are **sequenced across phases**, not all fired at t=0.
- **Stillness is only the 2-3 frames allocated in Video direction**; every other frame develops mid-shot.
- Each visual frame's `asset_candidates` have a `focal` + per-candidate `roles`; none added or dropped.
- **Video direction** stated once at the top (palette · shot model + idle budget · negative list incl. both failure modes · stillness allocation); per-frame entries are deltas, not restatements.
- **`## Video direction`** written once at the top (palette · motion grammar + shot model + idle budget · stillness allocation · negative list incl. both failure modes); per-frame entries are deltas, not restatements.
- Every frame is a **time-coded shot sequence** with real second windows across its `duration` — not a tag bag.
- **No frame front-loads** — at t=0 only what the VO is saying enters; each further piece reveals on its spoken cue, across the back ~50%. Window count follows the VO, not a fixed number.
- Every frame names an **`blueprint:`** id (Reproduce / Adapt) or `compose`; an Adapt states keep/change and **keeps the signature move**; nothing collapses to a single front-loaded dump — reveals stay paced to the VO.
- **Held frames are deliberate** — allocated in Video direction for rhythm; a held read is fine (prefer stillness to bad motion), but no frame may be front-loaded-then-frozen.
- Each frame's `asset_candidates` have a `focal` + per-candidate `roles`; none added, swapped, or dropped.
- Layout named **inline** per Scene (template / density / depth / hierarchy — the **Layout** vocabulary here); motion named **inline** per Scene from the vocabulary (`motion-language.md`). No px / ease curves / ms / JS.
- Content planned into the top ~83% (caption band clear).
- Palette / type pulled from `frame.md` by role — nothing invented.
- You wrote no HTML and never read `capture/`.