| name | create-storyboard-html |
| description | assemble <video_folder>/storyboard.html as the single review surface for a video project. Reads per-state JSON fragments from _state/ and templates them into one collated, browsable page with TOC + state sections. |
create-storyboard-html
Purpose
Every video project's storyboard.html is the single review surface — the operator opens it once and sees the latest state of every gate (idea brief → lyric/script → keyframes → animations → master). Per STORYBOARD.md at the repo root and the music-video-ad molecule's doctrine, all assets/handoffs/review notes land in this one file.
Hand-authoring the HTML across many states drifts. This atom replaces that with a fragment + render contract: each state writes a small JSON fragment under <video_folder>/_state/NN-<slug>.json, and scripts/render_storyboard.py collates them into the final HTML.
When to use
- Initializing a new ad — write a State 0 fragment and render the shell so the operator has a URL to bookmark from day one.
- After every state advance — append the new state's fragment, re-render. The pipeline already runs
update-storyboard as a post-step in music-video-ad and create-cartoon-music-video; this atom is what those post-steps call.
- Reviewing —
open <video_folder>/storyboard.html.
Use the legacy hand-authored route only if the project is one-off or the fragment kinds don't cover the format. For music-video, cartoon-music-video, comic-strip, and most narrator-driven ads the fragment kinds below are sufficient.
Fragment schema
<video_folder>/_state/<order-prefix>-<slug>.json. The order prefix decides where the section lands in the rendered HTML — use the orchestrator's state numbering (1-, 2-, 2.5-, 2.6-, 3-, 3.5-, 5-, 5.5-, 7-, 7.4-, 7.5-, 8-, 9-, 10-).
{
"state": "5.5-animate",
"label": "State 5.5 · Animate",
"summary": "17 clips, Seedance 2.0 with Veo fallback on 4.",
"kind": "scenes",
"scenes": [
{
"id": "scene-01", "bar": 1, "timecode": "00:00–00:03.3",
"lyric": "what's the move",
"keyframe": "generated/keyframes/scene-01.png",
"animated": "generated/animated/scene-01.mp4",
"prompt": "the felt amigurumi woman LIFTS her phone, slow push-in",
"notes"
Other kinds:
| kind | required fields | renders as |
|---|
html | html (trusted raw body) | drop-in HTML block |
lyrics | lyrics: ["line", …] (+ optional accent_words) | serif lyric block, accent words italic-bold in the accent color |
scenes | scenes: [{id, lyric, keyframe, animated?, prompt?, notes?, timecode}] | responsive grid; auto-swaps animated→keyframe if mp4 missing |
metrics | metrics: [{label, value}] | small two-column key/value table |
video | video: <relpath> (+ optional video_caption) | inline 9:16 controlled <video> |
audio | audio: <relpath> (+ optional audio_caption) | inline <audio controls> |
image | image: <relpath> (+ optional image_caption) | inline <img> |
Missing assets render as a labeled orange placeholder so the gap is visible. Asset paths are relative to <video_folder>.
Workflow
- State N produces its artifacts (lyrics file, keyframe PNGs, master mp4, etc.).
- State N writes (or rewrites)
<video_folder>/_state/<order>-<slug>.json reflecting its current artifacts.
- State N calls:
python3 skills/atoms/planning/create-storyboard-html/scripts/render_storyboard.py \
--video-folder <video_folder> \
[--title "Brand · Format"] \
[--accent-color "#0052FF"]
- Storyboard is now current. State N + 1 repeats the cycle.
Output
<video_folder>/storyboard.html
- A TOC of state sections at the top.
- One
<section> per fragment, ordered by the fragment filename prefix.
Quality Checks
- Every fragment that references an asset path resolves to a real file under
<video_folder> (otherwise the page renders an explicit "missing" placeholder, which is the loud signal).
- The fragment filename's order prefix matches an orchestrator state number — drift means the section sorts out of order.
- The page opens in a browser without a server (relative paths, no external CDN).
_state/*.json files are valid JSON (the renderer skips malformed ones with a warning, but they should never ship malformed).
Failure Modes
- A state appends a fragment but forgets to re-run the renderer — the storyboard is stale. The orchestrator's
update-storyboard post-step exists to prevent this.
- Two fragments share the same order prefix — they sort lexicographically within the prefix; pick distinct slugs.
- Asset paths leak
.. outside the project — paths are not sandboxed; trust the pipeline that writes the fragments.
- Operator hand-edits
storyboard.html and gets stomped on the next render — never hand-edit; only fragments are durable. If you need a one-off HTML insert, write a kind:"html" fragment.
Composition
This atom is wrapped by:
molecules/music-video-ad — runs render_storyboard.py after every state.
molecules/create-cartoon-music-video — same.
video-orchestrator/create-storyboard — the standalone storyboard-only entrypoint for non-molecule runs.
The legacy free-form storyboard.html hand-writing pattern is deprecated in favor of fragments. For per-scene keyframe-only review pages (not the full single-review-surface), use atoms/planning/render-keyframe-storyboard — that atom has a different schema scoped to one phase.