| name | create-silent-product-demo |
| description | Produce a 12-20s music-only 9:16 product demo Reel from the dominant silent-product-demo archetype — one static hook caption holds full duration, feature-flash bursts show the product in action, and the hero moment gets a payoff hold with the biggest cut hidden in a silence-masked music gap. Accepts either generative footage (Nano Banana still + Seedance i2v) or a conform re-edit of real product footage. Use for any physical or digital product where music-driven action speaks louder than VO. |
create-silent-product-demo
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-hook title 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
produce beat-grid.json. Required because silent-product-demo is
beat_sync: true.
atoms/planning/generate-edit-decision-list — consume the shot list +
beat-grid and produce a validated edl.json (archetype silent-product-demo,
grammar.loop: false). Sets the payoff-hold clip, silence-masked cut, and
static-title-card caption.
atoms/assembly/render-edl — execute the edl.json into master.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 before
extract-beat-grid.
atoms/music/create-music-elevenlabs — fallback music generation when
find-music yields 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's sfx[] array.
atoms/captions/style-captions — confirm the static title card is rendered
with the bold-tiktok-hook preset at the correct safe-area position.
atoms/review/watch — frame-grounded QC of the final master.
Workflow
Phase 0 — Intake
- Read
hook_caption and shot_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 no product_reference is
provided, STOP and ask.
- If
"conform", validate every source_clips path exists on disk.
- Set
target_duration (default 15). Validate it is in the 12-20s range.
- Create
output_dir with canonical 5-folder structure:
source/, assets/, finals/, working/, archive/.
Phase 1 — Music + beat grid
- Music selection:
- If
music_path is provided, skip to step 2.
- Otherwise run
find-music with a brief matching the product mood. If
find-music returns a suitable track, use it. If not, run
create-music-elevenlabs with a prompt derived from the shot list
(bright/energetic instrumental, target BPM 100-140, target duration
target_duration + 2s).
- Write the music file to
assets/music/track.mp3.
- Run
extract-beat-grid on the music track:
python3 skills/atoms/audio-editing/extract-beat-grid/scripts/extract_beats.py \
--source assets/music/track.mp3 \
--output-dir working/beat-grid/
Record bpm and verify beats is non-empty. Warn if BPM < 80 or > 180
(unusual for this archetype).
- Write
working/beat-grid/beat-grid.json path for use in Phase 2.
Phase 2 — Generate edit decision list
- Build a scene table JSON at
working/scene-table.json:
[
{ "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" }
]
The payoff-hold scene must be positioned in the 60-85% window of
target_duration (i.e. roughly target_duration × 0.6 to
target_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>
Phase 3 — Generate or trim scene clips
Generative mode:
For each scene clip listed in edl.json:
- Run
create-image-nano-banana-fal on the product_reference with the
clip's source.spec.description as the style prompt. Save keyframe to
assets/keyframes/<id>.png.
- If the clip's
target_out - target_in > 2.0s and the scene requires
visible product motion (pouring, pressing, spraying), run
create-video-seedance with the keyframe → short motion clip. Save to
assets/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-edl handles
static-image clips.
- Apply
add-zoom-cuts to feature-flash clips in the hook segment (clips
with role: "hook") for a zoom-punch post-motion effect.
Conform mode:
For each scene clip listed in edl.json:
- Run
trim-video-clips to extract the target range from the
source.file at source.in to source.out.
- Save trimmed clip to
assets/clips/<id>.mp4.
- Apply
add-zoom-cuts to 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-ffmpeg to generate the whoosh or tap SFX at the
specified at timestamps.
- SFX are passed to
render-edl via 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.0 and 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.mp4 exists and is non-zero; ffprobe reports both video
and audio streams.
- Duration of
master.mp4 is within ±0.5s of target_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.json exists with status: pass and all clips_rendered paths
resolve on disk.
- Self-QC punch list from Phase 6 has zero critical issues.
- For conform mode: every
source.file path in edl.json exists.
- For beat-synced clips: all
beat-synced-cut target_in values are within
±50ms of a grid bar boundary in beat-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.py
before generation.
- AI generates Tier 3 footage (hands, liquid physics) — reroll rates are
coin-flip at best (LEARNINGS.md #6). Switch to
conform mode 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-edl reports missing generate-mode clips — one or more
assets/clips/<id>.mp4 not generated. Re-run Phase 3 for the missing clips
before re-running the renderer.
edl_validate.py exits 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_s explicitly in the EDL, or choose
a track whose drop naturally falls in the 60-85% window.