make-video · git:20261104.ba432e7 · 2026-11-04 · sha256 b5b2570946706766
make-video git:20261104.ba432e7A
Immutable. This exact content never changes and is served at /api/v1/blob/b5b2570946706766.
---
name: make-video
description: Make a marketing video, reel, ad, post or carousel for any brand in brands/. Covers the whole path - brief, art direction (a look from looks/, different from the brand's recent pieces), storyboard, approval, plates and audio, composition, render, frame-by-frame QA and a documented delivery folder. Use for any "make a reel / ad / post / video for <brand>" request.
---
# Making a marketing video
## Start
1. No `core/render.mjs` in the project → run the `setup` skill first. Read `STATUS.md` (open work, decisions, learnings).
2. Find the brand in `brands/<slug>/`. Read its `README.md` and `compliance.md`. No brand folder yet → run the
`new-brand` skill first. Never borrow one brand's look, facts or voice for another.
3. Create `productions/<brand>/<ID>-<slug>/` from `productions/_TEMPLATE/`. IDs: `REEL-###`, `POST-###`, never reused.
## 1. Directions, storyboard, then stop
**Pick the look first** (`looks/README.md`). The brand gives identity (facts, logo, colours, fonts, voice); the look
gives the art direction (palette treatment, type, layout, motion, texture, engines, sound). A brand's pieces should
not all look alike:
1. `node scripts/looks.mjs <brand>`: what the brand's earlier pieces used and what it has not tried. Read the brand's
*Look range* (fits, ask first, never).
2. Section 0 of the README: pitch **2–3 directions that differ from each other** (e.g. neon-noir vs photoreal vs
paper-collage), at least one the last two pieces did not use, each with one line on why it fits the brief. Render
one style frame per direction: the look's starter with the brand's tokens and the real hook line → `qa/directions/`.
Look at them side by side; if two could be mistaken for each other, replace one.
3. Reuse the brand's last look only when the person asks or the piece is part of a series (say so).
Then fill sections 1–4 for the direction the person likes best (or for each, briefly, if they want to choose from
storyboards): the problem, the benefit, 3 hook variants, one row per beat (time, visual, **engine**, on-screen text,
sound), caption. Pick engines with the `pick-engine` skill and say why; the look's README names its usual engines.
State size, length, placement and sound (VO / music / silent), and the other formats wanted (4:5 feed, 1:1, 16:9):
one composition renders them all.
**Stop and ask for approval** in chat: the look and the storyboard. No plates, audio or composition before the box is ticked. The first time in a
project (no `review.json` in any production yet), offer the desk in one line: "Prefer to review on a page? I can open the desk." On a yes, the
`review-desk` skill opens it, and they may approve there. Never tick the box on your own.
## 2. Produce
| Step | How |
|---|---|
| Plates | 3D: `scene-3d` skill (Three.js live, or Blender → MP4). Live site: `capture-site` skill. Footage: `Studio.video` |
| Character | `character-2d` skill (SVG rig, Lottie, Rive, lip sync) |
| Audio | ElevenLabs MCP (`.mcp.json`) for VO, SFX and music → `audio/`. Cue sheet `audio/cues.json` → `python3 scripts/mix_audio.py audio/cues.json audio/mix.wav`. Word timings for captions: `scripts/transcribe.sh`. Mouth shapes for a character who speaks a line: `scripts/lipsync.sh` (one file per line, offset = the line's start in `cues.json`) |
| Beat grid | Music → `node scripts/beats.mjs audio/music.mp3 audio/beats.json`. In the composition `await Studio.beats("../audio/beats.json", { offset: <music start> })`, then cut on `Studio.bar(n)` / `Studio.beat(n)`. Check the BPM it prints against the track |
| Captions | VO pieces: `Studio.captions(el, "../audio/vo.json", { offset: <VO start>, style: "karaoke" \| "pop" \| "highlight" })` from the whisper JSON. Check the words against the script (whisper mishears names) |
| Composition | `composition/index.html`, started from `looks/<look>/starter.html`, **never from an earlier production's composition** (that is how every piece ends up the same). Load `/core/lib/base.css`, then `/brands/<slug>/tokens.css`, then `/looks/<look>/look.css` (+ `/looks/fx.js` for texture). Keep the look's type, motion and texture; bring in the brand's logo, product and copy. Engine patterns: `examples/engines-demo/index.html`; captions, beats, transitions and formats: `examples/reel-layer/index.html`. Text and CTA inside `.safe` (paid) or `.safe-organic` |
| Formats | `Studio.setup({ …, formats: ["4x5", "1x1", "16x9"] })`; per-format CSS under `.fmt-4x5` etc., values with `Studio.pick({...})` |
| Render | `node core/render.mjs video <comp> export/<ID>-<lang>.mp4 --audio audio/mix.wav` (posts: `still`). Other formats: `--formats all` → `export/<ID>-<lang>-4x5.mp4` … QA each one |
| Motion blur | On for any piece with fast moves (pushes, whips, spins, flying type): `Studio.setup({ …, motionBlur: true })`, 16 samples for whip moves. Renders get about 8× slower: pass `--motion-blur 1` for quick checks, keep it for the final render and its QA |
## 3. QA (never skip)
1. `scripts/qa-video.sh export/<ID>.mp4 qa` → `qa/report.md`. Every ❌ fixed (a black first frame, more than 3
flashes a second). Every jump either an intended cut (say so in the report) or fixed.
2. `node core/render.mjs text-qa <comp> qa` → `qa/text.md` (exits 1 on a ❌): the hook readable by 3 s, text at
least 28 px (aim 40+), contrast 3:1 for large text and 4.5:1 below 48 px against what is actually behind it, nothing
under the Reels UI. Every ❌ fixed, or explained in the report (e.g. a label meant to be a texture). Mark pure
decoration `data-qa-skip`. Look at each `qa/flagged/text_*.jpg`. Canvas text (Pixi, Three, Rive) isn't measured.
3. `node core/render.mjs stills <comp> qa/beats --at <every beat start and end> --safe`, then **look at each one**:
text inside the safe zone, nothing cut off, no half-loaded asset, no overlap, spelling, brand colours, and the
look held: its type, motion and texture all the way through (no beat drifting back to a previous piece's style).
4. Determinism spot check when a WebGL or plate engine is used: render one frame twice, the md5s match.
5. Listen once with sound: VO in sync with text (and with the mouth, if a character speaks), music ducked under
VO, −14 LUFS.
6. Fact check: every claim in section 6 of the README has a source. Compliance list passes.
## 4. Deliver
Fill README sections 5–7 (production log, fact check, rebuild commands). Status `delivered`. Update `STATUS.md`:
the production row (with its look), anything learned (what worked, an engine quirk, a look that needed tuning) in
*Learnings*.
## Defaults (state them in every storyboard)
| | |
|---|---|
| Reel / video ad / Story | 1080×1920, 9:16, 30 fps CFR, H.264, −14 LUFS. Paid 9–20 s, organic 15–45 s |
| Post / carousel | 1080×1350, 4:5 |
| Safe zone, paid Reels | top 14%, bottom 35%, sides 6% (`.safe`) |
| Safe zone, organic Reels | top 11.5%, bottom 22%, left 6%, right 11% (`.safe-organic`) |
| Silent pieces | All meaning on screen; captions ≥ 42 px; each line held ≥ 1.5 s |
| VO pieces | Burned-in captions (`Studio.captions`): many people watch with the sound off |
| Other formats | 4:5 feed 1080×1350, 1:1 1080×1080, 16:9 1920×1080; `.safe` is a 6% margin (16:9: 8% top/bottom, 5% sides) |