Compress a spoken sentence into a sharp visual idea, then turn it into an editorial metaphor collage animation assembled piece by piece from an empty color field.
It is a sister skill to ray-cover : they share the same "editorial metaphor collage" visual system—cover produces static covers, broll produces dynamic B-roll. They follow the same de-branding convention: do not imitate the recognizable signatures of specific brands or active artists; uniformly translate "Vox"-type requests into describable editorial collage mechanisms (halftone dots, cardstock, keylines, color fields).
Default pipeline:
- Design only the visual metaphor and wait for user confirmation (Gate 1)
- Generate only the final still frame and wait for user confirmation (Gate 2)
- Call the video model to do first-and-last-frame assembly animation and complete QA (Gate 3)
The three gates are ordered by increasing cost: metaphor is free, still frames are cheap, video is the most expensive. In batch mode, only release the items that pass. When the user explicitly authorizes "just run it all the way through without stopping," it can execute fully automatically, but the QA document for each Gate must still be written as usual.
Environment self-check
Run this first on every trigger:
bash <本skill目录>/scripts/check_setup.sh
If all PASS, start directly; if there is a FAIL, only list for the user the configuration method for the missing items, and re-run the self-check after configuration. Two pitfalls found in practice:
- The agent session's shell environment is a snapshot from startup. When the user has just written
GEMINI_API_KEYinto~/.zshrc, the command must explicitlysource ~/.zshrcfor it to be visible. - The video script's download step reports
CERTIFICATE_VERIFY_FAILEDin a macOS venv; this skill'sgenerate_video.pyalready has certifi auto-wiring built in, so there is no need to manually setSSL_CERT_FILE. Whenpip installreports the same error in a venv, useSSL_CERT_FILE=$(venv/bin/python -m certifi) pip install ...。 - In bash batch scripts, do not
set -e+source ~/.zshrc—zsh syntax entering bash will error and silently abort the entire script (manifesting as a batch task failing directly with no output). The correct approach is to extract only the variables needed:export GEMINI_API_KEY=$(grep -o 'GEMINI_API_KEY="[^"]*"' ~/.zshrc | cut -d'"' -f2)。
Model
- Still frames:
gemini-3-pro-image(quality first; cheaper alternativegemini-3.1-flash-image) - Video:
gemini-omni-flash-previewfirst-and-last-frame interpolation (script default; do not switch to Veo unless the user names it) - Narration:
gemini-3.1-flash-tts-preview(scripts/generate_tts.py, default Charon voice)
All three share the same GEMINI_API_KEY, billed by usage. Measured order of magnitude (2026-07): video at about $0.3 per clip is by far the largest share, still frames with 3-pro at about $0.11 per image, TTS negligible; an 8-beat explainer video is about $3.5.
Cost discipline
- On failure, rerun only the failed job; never rerun the whole batch—generation is billed server-side, and a failed download has already been charged
- At each Gate 3, record one line of cost accounting in the QA document: number of generations × model (including discarded clips), so costs are visible
- Cost-reduction directions ordered by leverage: pilot switching still frames to the flash tier → change simple rigid-body motions (slide-in/snap-in type beats) to
HyperFrames deterministic animation, leaving the video model only for shots with physical texture (bursting/penetration) → only consider the
Flow subscription credit route during mass production. The latter two have both been proven in practice; for operational details see
references/flow-runbook.md(L2 Flow credits + L3 HyperFrames sticker separation pipeline) - On the API side, note the AI Studio project monthly spend cap: once it is hit, all calls return 429, raise the limit at ai.studio/spend or wait for the reset at the start of the month; during the outage, Nano Banana 2 inside Flow can serve as a fallback for still-frame/sticker generation
Success criteria (visual grammar)
- One sentence expresses only one clear metaphor, key objects no more than 4 groups
- The background is a strong, flat, even color field, with colors chosen by semantics; the same batch uses a unified design language but different base colors
- The subject uses black-and-white halftone photographic cut-outs as the skeleton, with local colored cardstock serving the information hierarchy
- All paper pieces have clear cut edges, a cream-white keyline, low-opacity soft shadows, and paper grain
- The action is assemble-from-empty, assembling piece by piece, not drifting, shaking, or slow zoom
- No subtitles, no full voiceover text, no logo, no watermark, no UI
- Default delivery is 9:16, 5 seconds, 720×1280, 24fps, silent MP4
Color semantics
| Base color | Semantics |
|---|---|
| Burnt orange / red | Time consumption, labor, urgency |
| Mustard yellow | Tools, warnings, experience leakage |
| Ink green / deep cyan | Cognition, judgment, collaboration, automated execution |
| Deep purple | Specifications, accumulation, long-term memory |
Accent colors are uniformly chosen from the cream-white / mustard-yellow / orange family; in batches, do not each go their own way.
Project directory
~/hyperframes-projects/YYYY-MM-DD-collage-broll-标题/ # 北京时间命名
├── brief.md # 文稿 + Gate 1 隐喻记录
├── omni-jobs.json
├── gate2-qa.md / gate3-qa.md
├── still-contact-sheet.jpg
├── omni-contact-sheet-all.jpg / video-first-frame-all.jpg / end-frame-comparison-all.jpg
└── 01-概念名/
├── imagegen-prompt.txt / omni-prompt.txt
├── frames/{last-frame-original.png, first-frame.png, last-frame.png, bg-hex.txt}
└── omni/run-v01/{final-5s.mp4, final-5s-noaudio.mp4, contact-sheet.jpg,
video-first-frame.jpg, video-last-frame.jpg, end-frame-comparison.jpg}
Gate 1: Metaphor design
For each script, extract the core meaning, emotion, and action verbs, and compress them into one visual proposition. Deliverables: core meaning / emotion / one-sentence visual proposition / ≤4 groups of key objects / suggested base color and accent color / expected assembly order. Then stop and wait for confirmation.
Tested anti-drift experience:When there are more than 4 groups of objects, or when there are tangled/interwoven spatial relationships (such as film wrapped around scissors), the video model is more likely to rearrange the composition. For entries that require strict placement, use fewer groups + flat relationships; only entries that can accept slight drift may be more complex.
For batch metaphors, prioritize forming a narrative arc from beginning to end (e.g., manual consumption → process accumulation → automated execution), and change the base color as the narrative progresses.
Gate 2: Generate still frames
Imagegen prompt template
Use case: ads-marketing
Asset type: final still frame for a 9:16 image-to-video B-roll clip
Primary request: Create a finished editorial paper-collage image expressing [一句话视觉命题].
Scene/backdrop: perfectly flat [颜色] paper field [hex] with subtle uncoated paper fiber.
Style/medium: premium editorial stop-motion paper collage; black-and-white halftone photographic cut-outs mixed with selective [点色] colored cardstock.
Composition/framing: vertical 9:16 locked poster frame; central subject within the middle 70 percent; generous clean color-field negative space; [N] large separable paper groups for later assemble-from-empty animation.
Materials/textures: visible printed halftone dots, crisp machine-cut edges, thin warm-cream paper keylines, soft low-opacity physical drop shadows.
Constraints: [本条隐喻必须一眼看懂的关系].
Avoid: no typography, no readable letters, no numerals, no logos, no watermark, no UI, no subtitles, no glossy 3D, no photoreal environment, no clutter.
Hard constraints (tested):Objects such as sticky notes, cards, and book pages must explicitly add "carrying only abstract wavy squiggle doodle lines — absolutely no letters or words", otherwise fake text is highly likely to appear; once fake text appears, regenerate the still frame, and do not expect the video prompt to fix it.
Generation and QA
~/hyperframes-projects/.omni-venv/bin/python <本skill目录>/scripts/generate_image.py \
--prompt-file <item>/imagegen-prompt.txt \
--output <item>/frames/last-frame-original.png --aspect-ratio 9:16 \
[--style-ref <第一张过审静帧>]
During batches, run in parallel in the background (& + wait)。style anchor: for batch projects, first generate 1–2 test images, and pass the first approved still frame as --style-ref to the remaining generations — consistency changes from a prompt prayer into a reference-image mechanism. QA checklist: the metaphor is readable at a glance / the subject is focused / no fake text, logo, watermark, or UI / sufficient negative space in the color field / ≤4 clear large groups / consistent texture within the same batch. Generate a numbered contact sheet and show it to the user, then stop and wait for Gate 2 confirmation. For regeneration rounds, increment v2, v3, and keep the old versions for comparison.
Gate 3: Generate video
1. First and last frames (key pitfall: use measured values for the base color)
The base color in the generated image is always slightly darker than the hex in the prompt.The actual base color must be sampled from the generated imageMake the first frame empty, otherwise the first frame will jump in color when it reaches the assembly scene. It has been solidified into a script:
bash <本skill目录>/scripts/prepare_frames.sh <item-dir> [采样点x:y]
One command completes sampling (4×4 mean → frames/bg-hex.txt), 1080×1920 end frame, solid-color empty first frame. The sampling point defaults to the upper-left corner 28:58; when multiple objects are close to the edge, switch the coordinates.
2. Omni animation prompt template
Paper-collage stop-motion assembly, using Image 1 as the exact empty first frame and Image 2 as the exact completed last frame. In one continuous locked-off vertical shot, open on the empty flat [color] paper field.
Assemble the scene piece by piece with crisp physical stop-motion timing: [按组装顺序描述各组如何 slide in / snap into place / 完成动作]. End by holding the supplied completed composition.
Preserve the exact 9:16 framing, [实测hex] color field, [点色] cardstock accents, uncoated paper grain, halftone dots, cream keylines, crisp cut edges and soft shadows. Restrained tactile 2D paper craft only.
No scene cuts, no camera movement, no zoom, no morphing, no new objects, no text, no letters, no numbers, no logos, no watermark, no UI, no sound.
3. Batch invocation
video-jobs.json For each job:{prompt, image: [first-frame, last-frame], output, aspect_ratio: "9:16", duration: 5}, optional provider(default google;fal goes to aggregation) and model(for fal:kling-o3-standard / kling-o3-pro / seedance-2.0 / seedance-2.0-fast)。
~/hyperframes-projects/.omni-venv/bin/python <本skill目录>/scripts/video_dispatch.py \
--batch <project>/video-jobs.json --concurrency 3 --resume
The dispatcher distributes by provider group: google → generate_video.py(existing behavior unchanged, old omni-jobs.json directly compatible); fal → fal_driver.py(requires FAL_KEY, first and last frames are uploaded inline as data URIs).Only models that explicitly support the last-frame parameter enter fal_driver.py 's MODEL_MAP—if the last frame is not supported, it is better to reject the job than to silently degrade to single-image video generation.
--resume: jobs whose output already exists and passes ffprobe validation are automatically skipped—always include it when rerunning; "only rerun failed jobs" is guaranteed by the tool rather than by discipline- Automated cost accounting: each run appends the estimated cost of newly produced jobs to the
cost-ledger.jsonlin the same directory as jobs and prints a summary (fal is priced using MODEL_MAP, google uses an estimated value, which can be overridden withGOOGLE_VIDEO_USD_PER_SEC); the Gate 3 QA document only needs to reference the summary line - If a legacy Interactions API schema error appears on the google side, it means the old SDK is being used; switch to the shared venv's python
4. Mandatory silent delivery + QA artifacts
bash <本skill目录>/scripts/qa_video.sh <成片.mp4> <确认静帧.png> [输出目录] [bg-hex]
One command completes: silent version, per-second contact sheet, real first/last frames, last-frame comparison image, and outputs twoobjective scores(qa_score.py: first-frame purity ≥0.97 is a pure empty scene; last-frame restoration ≥0.90 is high restoration—this score measures composition-level consistency with the confirmed still frame. When HyperFrames recomposes, a lower score is normal; use it only as a reference. The score goes into the QA document as supporting evidence, not as a replacement for visual inspection.
For batch projects, also run overview.py --project <dir>(reads beats.json) to merge three overviews: item-by-item contact sheet vstack, real first frames in a horizontal row (to verify that all start from an empty scene), and last-frame comparison vstack.
Video QA standards
- The first frame is close to a solid-color empty scene; slight early exposure at the edges is acceptable
- Item-by-item assembly is visible in the middle section, not an overall fade-in
- No cuts, zoom, 3D-ification, realistic drift, or fake text
- The last frame is consistent with the confirmed still frame; slight pose drift does not affect the metaphorical semantics and is judged as passed,do not rerun for this; if moderate composition rearrangement (object displacement and scaling) has complete semantics, judge it as "passed (with notes)" and explain in gate3-qa.md
- 720×1280, 24fps, 5 seconds, zero audio track
Write the QA conclusion item by item into gate2-qa.md / gate3-qa.md, including the rationale for approving with defects.
Full explainer video mode (beat map → 45–60 second finished video)
A second type of output beyond single-sentence B-roll. Input a topic (or a finished-draft judgment from ray-writer / ray-kb), and produce a complete vertical explainer video with narration, subtitles, and content-aware pacing. The whole = beat map layer + per-beat passing through the above three gates + assembly layer.
1. Beat map (the full-video version of Gate 1)
Split the topic into 8–10 beats, each beat with one line of voiceover (20–30 characters) + one metaphor. Narrative discussion goes in brief.md,structured data goes in the project root beats.json(single source of truth, see schema at
references/beats-manifest.md; prepare / assemble / overview / qa all read it):
- Narrative arc: first select by content type from the 14 arcs in
references/narrative-arcs.md(opinion→opinion, marketing→pas/aida, popular science→myth_buster, history→timeline, people→origin/story_spine…), the arc's beat sequence is therolesequence, and the role determines the tempo tier and visual weight - Visual theme (Gate 1.5 bake-off): pick 2–4 candidate sets from
references/theme-presets.md, render one image per set for the same beat for the user to choose, the winning preset locks the whole film and serves as the--style-refstyle anchor; after the film is done you can swap presets to restyle and reskin - In-frame titles: hook / closing beats can be paired with
headline(paper-cut cardboard large text,render_headline.pydeterministic rendering, zero fake Chinese characters; AI-generated visuals continue to ban text) - Generation routing: tag each beat with
route—rigid-body slide-in/snap-in/stacking type assembly goes throughhyperframes(zero generation cost, deterministic, 7.5s rendering, pipeline seereferences/flow-runbook.mdL3), bursting/penetration/deformation/organic motion go throughvideo-model. In an 8-beat film usually over half the beats can go through hyperframes, cutting per-film cost from ~$3.5 to ~$1 - Color narrative: adjacent beats must have different background colors, and colors advance with emotion (example: yellow question → cyan scene → orange risk → red breakthrough → purple leap → green mechanism → yellow synthesis → purple ending)
- Abstract-concept beats ("two kinds", "three levels") are the worst offenders for clutter; Constraints must use the sentence pattern "The image contains ONLY these N groups and absolutely nothing else" + Avoid enumerating contaminating objects (trophies, clocks, hearts, compasses, arrows, hands, currency symbols) + require ≥40% empty color field
2. Narration and speed variation
One TTS per beat (scripts/generate_tts.py, can be parallel).Do not adjust speech rate with a style prompt
(measured only ±2%), at the assembly layer use ffmpeg atempo to vary speed by role tier:
| beat role | atempo | tail pause |
|---|---|---|
| hook (opening question) | 1.04 | 0.35s |
| definition / explanation / transition sentence | 1.09–1.10 | 0.35s |
| authoritative judgment / punchline | 1.05 | 0.50s |
| synthesis | 1.07 | 0.35s |
| closing rhetorical question / punchline | 1.02 | 0.85s |
Principle: important words slow, transitional words fast; emphasis is hammered home with "slow + pause", and the ending leaves the audience departing with a question.
3. Subtitles
scripts/render_caption.py(Pillow + PingFang, two-line format, cream-white text + semi-transparent black rounded box)
render one subtitle PNG per beat, overlay onto (W-w)/2:H-h-170during assembly. Does not depend on ffmpeg's
drawtext (many local builds don't have it compiled).
4. Assembly
python <本skill目录>/scripts/assemble.py --project <dir>
Fully automatic reading of beats.json: ffprobe measures actual narration duration (eliminates manual transcription) → role table derives
tempo/tail → beat duration = max(视频长, lead + 旁白/tempo + tail), shortfalls are filled by
tpad freezing the tail frame → automatically render missing subtitle PNGs → single filter_complex assembly →
volumedetect check (target max ≈ -1~-2dB) → write assembly/assemble-report.json.
Explicit tempo/tail in the manifest can override the role table. Rhythm feedback only changes the manifest and reassembles,
zero regeneration of assets, zero cost。
Common issues
- Weak assembly feel → reduce the number of object groups, change the prompt to an explicit piece-by-piece slide in / snap into place sequence
- End-frame drift → reinforce "Image 2 is the exact completed last frame"; if it still drifts, go back to Gate 1 and simplify the spatial relationships
- Fake text appears → go back and regenerate the still frame (add a no-text constraint), do not patch it with the video prompt
- Under a strict empty-scene requirement, the first frame shows edges → use HyperFrames to fill in the preceding segment
- Texture leans realistic / 3D → reinforce in the prompt "Restrained tactile 2D paper craft only"
When not to use it
- Need precise layers, occlusion, camera fly-through, or an editable timeline → HyperFrames
- Only want the prompt, not the finished video → write the prompt directly
- Real-person talking-head, live-action product ads → do not use this workflow
Source
The workflow and video script are adapted from gbro-collage-broll(MIT, © 2026 狗哥笔记), adapted for the Claude Code environment: still-frame generation now uses the Gemini image API (originally relied on Codex's built-in image_gen), with built-in certifi SSL fixes and a tested background-color sampling process. Similar open-source references:vox-director。