Purpose
Produce a shippable 12-20s silent (music-only) product demo Reel using the
silent-product-demo archetype from CREATOR_GRAMMAR.md §8. The archetype's
defining pattern:
- One static caption holds the full duration — the hook claim rendered as a
bold-tiktok-hooktitle card. No VO, no karaoke captions, no burned-in CTA. - Feature-flash bursts (5-8 hard cuts under 1s) showcase product features in rapid succession, bracketed by holds.
- One payoff hold — the hero moment (product working, the reveal, the money shot) runs 2–4× the mean shot length.
- Silence-masked cut hides the biggest edit in a 0.3–0.6s gap in the music.
- Beat-synced cuts throughout; cut density front-loads into the hook segment.
The molecule supports two footage modes:
- Generative — scenes generated via
create-image-nano-banana-fal+create-video-seedance. Best when no real product footage exists. - Conform — existing product footage re-cut via
trim-video-clips. Best when the brand has a real shoot or UGC source.
This is CREATOR_GRAMMAR.md Tier 1 (fully reproducible now): the entire editorial layer — cut points, payoff hold, beat-sync, static caption — is AI-reproducible. Only the payoff-hold visual may require Tier 2 i2v if physics is critical.
Inputs
| Input | Required | Notes |
|---|---|---|
hook_caption |
yes | The one sentence that holds on screen. ≤7 words. No URL, no price. |
shot_list |
yes | 5-8 scenes describing what the product does. One line per scene: "<shot-size>, <subject>, <action>". The payoff scene is labeled. |
footage_mode |
yes | "generative" or "conform". |
source_clips |
conform mode only | Paths to raw product footage file(s). |
product_reference |
generative mode | Path to a product image. Drives create-image-nano-banana-fal keyframes. |
music_path |
no | Path to an existing music track. If omitted, find-music or create-music-elevenlabs is run. |
target_duration |
no, default 15 |
Target video length in seconds (12-20). |
output_dir |
yes | Project directory following the canonical 5-folder layout. |
Composed Atoms
atoms/audio-editing/extract-beat-grid— analyse the music track and producebeat-grid.json. Required becausesilent-product-demoisbeat_sync: true.atoms/planning/generate-edit-decision-list— consume the shot list + beat-grid and produce a validatededl.json(archetypesilent-product-demo,grammar.loop: false). Sets the payoff-hold clip, silence-masked cut, and static-title-card caption.atoms/assembly/render-edl— execute theedl.jsonintomaster.mp4. Handles hard-cut concat, audio bed (music + silence beats), and burns the static title card.atoms/music/find-music— source a royalty-free music track when none is provided (preferred: bright/energetic, 100-140 BPM). Used beforeextract-beat-grid.atoms/music/create-music-elevenlabs— fallback music generation whenfind-musicyields nothing suitable.atoms/image-generation/create-image-nano-banana-fal— render each scene keyframe from a product reference image (generative mode, Tier 1).atoms/video-generation/create-video-seedance— animate each keyframe into a short motion clip (generative mode, Tier 2 — use only when motion specificity is needed; static stills work for feature-flash shots).atoms/editing/trim-video-clips— trim and time-align raw footage clips to EDL target durations (conform mode).atoms/editing/add-zoom-cuts— apply zoom-punch post-motion on feature-flash cuts in the hook segment (optional, recommended for Tier 1 clips).atoms/audio-editing/synthesize-sfx-ffmpeg— add whoosh/tap SFX on feature-flash cut points per the EDL'ssfx[]array.atoms/captions/style-captions— confirm the static title card is rendered with thebold-tiktok-hookpreset at the correct safe-area position.atoms/review/watch— frame-grounded QC of the final master.
Workflow
Phase 0 — Intake
- Read
hook_captionandshot_list. Confirm the shot list has 5-8 scenes and one scene is labeled as the payoff (the hero moment). - Confirm
footage_mode. If"generative"and noproduct_referenceis provided, STOP and ask. - If
"conform", validate everysource_clipspath exists on disk. - Set
target_duration(default15). Validate it is in the 12-20s range. - Create
output_dirwith canonical 5-folder structure:source/,assets/,finals/,working/,archive/.
Phase 1 — Music + beat grid
- Music selection:
- If
music_pathis provided, skip to step 2. - Otherwise run
find-musicwith a brief matching the product mood. Iffind-musicreturns a suitable track, use it. If not, runcreate-music-elevenlabswith a prompt derived from the shot list (bright/energetic instrumental, target BPM 100-140, target durationtarget_duration + 2s). - Write the music file to
assets/music/track.mp3.
- If
- Run
extract-beat-gridon the music track:
Recordpython3 skills/atoms/audio-editing/extract-beat-grid/scripts/extract_beats.py \ --source assets/music/track.mp3 \ --output-dir working/beat-grid/bpmand verifybeatsis non-empty. Warn if BPM < 80 or > 180 (unusual for this archetype). - Write
working/beat-grid/beat-grid.jsonpath for use in Phase 2.
Phase 2 — Generate edit decision list
- Build a scene table JSON at
working/scene-table.json:
The payoff-hold scene must be positioned in the 60-85% window of[ { "scene_id": "s01", "description": "<shot-list line 1>", "role": "hook" }, { "scene_id": "s02", "description": "<shot-list line 2>", "role": "feature" }, ... { "scene_id": "sNN", "description": "<payoff scene>", "role": "payoff-hold" } ]target_duration(i.e. roughlytarget_duration × 0.6totarget_duration × 0.85). - Run
generate-edit-decision-list:archetype: "silent-product-demo"source_mode: "generative"or"conform"beat_grid: "working/beat-grid/beat-grid.json"target_duration: <target_duration>output_path: "working/edl.json"
- Validate that
edl.jsonpasses the validator. Check:- Exactly one clip has
role: "payoff-hold". grammar.caption_family: "static-title-card".grammar.caption_preset: "bold-tiktok-hook".- The caption cue has
text == hook_captionand spans 0.0 tometa.target_duration. - No karaoke-pop captions (EDL validation rule 7).
audio.beat_gridis set to the beat-grid path.
- Exactly one clip has
- If the validator fails, fix each error and re-run before proceeding.
Phase 3 — Generate or trim scene clips
Generative mode:
For each scene clip listed in edl.json:
- Run
create-image-nano-banana-falon theproduct_referencewith the clip'ssource.spec.descriptionas the style prompt. Save keyframe toassets/keyframes/<id>.png. - If the clip's
target_out - target_in> 2.0s and the scene requires visible product motion (pouring, pressing, spraying), runcreate-video-seedancewith the keyframe → short motion clip. Save toassets/clips/<id>.mp4. - For clips ≤ 2.0s or where motion is not required (static display, flat
lay, label close-up), use the still image directly —
render-edlhandles static-image clips. - Apply
add-zoom-cutsto feature-flash clips in the hook segment (clips withrole: "hook") for a zoom-punch post-motion effect.
Conform mode:
For each scene clip listed in edl.json:
- Run
trim-video-clipsto extract the target range from thesource.fileatsource.intosource.out. - Save trimmed clip to
assets/clips/<id>.mp4. - Apply
add-zoom-cutsto hook-segment clips as in generative mode.
Phase 4 — Add SFX
For each clip in edl.json that has a non-empty sfx[] array:
- Run
synthesize-sfx-ffmpegto generate thewhooshortapSFX at the specifiedattimestamps. - SFX are passed to
render-edlvia the EDL; no manual mixing step needed.
Phase 5 — Render master
Run render-edl:
python3 skills/atoms/assembly/render-edl/scripts/render_edl.py \
--edl working/edl.json \
--output finals/master.mp4 \
--project-dir <output_dir> \
--crf 18
Verify manifest.json exists alongside master.mp4 with status: pass and
clips_rendered paths all resolving on disk.
Phase 6 — Self-QC
Run atoms/review/watch on finals/master.mp4 with the following QC prompt:
"Check: (1) the static hook caption holds full duration without drift; (2) the payoff-hold shot is the longest clip by 2–4× the mean shot length; (3) no karaoke captions appear; (4) the silence-masked cut before the payoff is not jarring; (5) cuts in the hook segment land on beats; (6) the tail decelerates."
Report findings as a timestamp punch list. If critical issues are found, fix and re-render from the appropriate phase.
Decision Rules
DR-1 — Generative vs conform footage choice
Use the CREATOR_GRAMMAR.md §10 tiers:
| Footage tier | EDL source mode | Clip generation approach |
|---|---|---|
| Tier 1 (flat lay, label, push-button action) | generative |
Nano Banana still only — no i2v |
| Tier 2 (product motion, pour, spray) | generative |
Nano Banana still → Seedance i2v |
| Tier 3 (hands manipulating, liquid physics) | conform |
Real footage is mandatory — AI fails here |
Never spend i2v credits on a Tier 1 clip. If the shot description mentions hands, sustained physical manipulation, or liquid physics, switch to conform mode for that clip (hybrid EDL source mode is allowed — see EDL schema §"Source modes").
DR-2 — Payoff hold placement
The payoff-hold clip must be the single longest clip in the timeline. Its
duration must be 2.0×–4.0× pacing_curve.mean_shot_len. The rule: if the
money shot feels rushed after rendering, extend it — the payoff hold is never
wrong if it's the hero moment. Place it in the 60-85% window of the video.
Do not place it in the first 50% — the pacing curve front-loads for the hook,
not the reward.
DR-3 — Static caption placement
The bold-tiktok-hook caption must:
- Start at
target_in: 0.0and hold to the last frame. - Position in the upper-third (top 25% of the safe area) — not lower-center, which is the default for karaoke-pop. Override the preset's default position if needed.
- Be ≤7 words (longer text violates the "built sound-off-first" rule).
- Not appear on the payoff-hold or tail clips if those clips have their own on-screen product text (suppress to avoid two-layer collision per LEARNINGS.md #7).
DR-4 — Music tempo and energy
The music track should match the pacing curve: energetic in the hook/body,
building into the payoff drop, with a tail-deceleration after. The EDL's
audio.music_drop_s field must be set to the payoff-hold clip's target_in.
If find-music returns a track whose drop does not land at the payoff, trim
or crossfade the music in the render-edl audio build step.
DR-5 — Beat snapping
For beat_sync: true, every clip boundary in the body segment must snap to a
bar boundary within ±150ms. If the pacing calculation lands a clip boundary
more than 150ms from any bar, move the boundary to the nearest bar (even if
this changes the clip duration by up to 10%). Use the generate-edit-decision-list
beat-snapping step (Step 6 in that atom's workflow).
Output
<output_dir>/
├── source/ ← original product footage (conform mode) or reference images
├── assets/
│ ├── music/
│ │ └── track.mp3
│ ├── keyframes/
│ │ └── <id>.png ← Nano Banana keyframes (generative mode)
│ └── clips/
│ └── <id>.mp4 ← trimmed or generated scene clips
├── working/
│ ├── beat-grid/
│ │ ├── beat-grid.json
│ │ └── manifest.json
│ ├── scene-table.json
│ ├── edl.json
│ └── planning-notes.md
└── finals/
├── master.mp4 ← the delivered output
└── manifest.json
The finals/master.mp4 is the shippable Reel: 9:16, 1080×1920, h264 + AAC,
30fps, 12-20s, with the static title card burned in and no VO track.
Quality Checks
finals/master.mp4exists and is non-zero;ffprobereports both video and audio streams.- Duration of
master.mp4is within ±0.5s oftarget_duration. - Resolution is 1080×1920 (9:16), 30fps.
- Exactly one clip has
role: "payoff-hold"and its duration is 2.0–4.0×manifest.json::pacing_curve.mean_shot_len. - The static hook caption spans full duration (0.0 to
target_duration); no karaoke-pop captions appear in any frame. - Audio track is present; no silent sections longer than 0.7s except the deliberate silence beat before the payoff (allowed: 0.25-0.6s).
manifest.jsonexists withstatus: passand allclips_renderedpaths resolve on disk.- Self-QC punch list from Phase 6 has zero critical issues.
- For conform mode: every
source.filepath inedl.jsonexists. - For beat-synced clips: all
beat-synced-cuttarget_invalues are within ±50ms of a grid bar boundary inbeat-grid.json.
Failure Modes
- No product reference image provided (generative mode) — cannot run
create-image-nano-banana-fal. Request the image before starting Phase 3. Nano Banana requires a visual reference to generate on-brand keyframes. - Payoff window too short for the archetype's 2.5× multiplier — the shot
list may have too many scenes for the target duration. Remove one body
scene or shorten two feature clips. Validate pacing with
edl_pacing.pybefore generation. - AI generates Tier 3 footage (hands, liquid physics) — reroll rates are
coin-flip at best (LEARNINGS.md #6). Switch to
conformmode for that clip immediately rather than burning Seedance credits on retries. - Music track has no identifiable beats (BPM < 20 from
extract-beat-grid) — ambient drone / atonal tracks don't beat-sync. Replace with a track that has a clear rhythmic pulse. - Static caption collides with product text in the payoff-hold clip — two
text layers at the same position = both unreadable (LEARNINGS.md #7).
Suppress the title card on the payoff clip using
notes: "suppress-caption"in the EDL clip, or move the title card to the upper-third only. render-edlreports missing generate-mode clips — one or moreassets/clips/<id>.mp4not generated. Re-run Phase 3 for the missing clips before re-running the renderer.edl_validate.pyexits non-zero — do not proceed to rendering. Fix each reported error (contiguity, missing payoff-hold, wrong caption family) and re-run the validator.- Music drop does not align with payoff hold — the energy peak lands at
the wrong moment. Set
audio.music_drop_sexplicitly in the EDL, or choose a track whose drop naturally falls in the 60-85% window.