Skip to content
Back to skills

Machine Editorial Broll

ASecurity

Generate branded motion-graphics "slides b-roll" for Zalo's videos (VSLs, YouTube, teleprompter scripts, reels) using the Machine Editorial v3 system in the Remotion studio at /Users/zalo/dev/operator-broll. Use when the user says "slides b-roll", "b-roll for this script", "motion graphics for my video", "VSL-style slides", "machine editorial", or hands over a script/teleprompter text and wants the graphics cut. NOT for the video game b-roll layer on a delivered reel (that's dot-matrix-broll)...

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 27, 2026
developmenttypescriptpythongobashreactnodespringawsapi

Works with

  • cli
  • api

Security analysis

A92/100
  • mediumUses curl or wget to download content

Pro shows the line behind each finding and how to fix it

Scanned October 7, 2026

npx -y skills add zalogarcia/zalo-claude-code-setup --skill machine-editorial-broll --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Machine Editorial Broll?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Machine Editorial Broll
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/zalogarcia-machine-editorial-broll/badge)](https://www.skillsdirectory.com/skills/zalogarcia-machine-editorial-broll)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: machine-editorial-broll
description: Generate branded motion-graphics "slides b-roll" for Zalo's videos (VSLs, YouTube, teleprompter scripts, reels) using the Machine Editorial v3 system in the Remotion studio at /Users/zalo/dev/operator-broll. Use when the user says "slides b-roll", "b-roll for this script", "motion graphics for my video", "VSL-style slides", "machine editorial", or hands over a script/teleprompter text and wants the graphics cut. NOT for the video game b-roll layer on a delivered reel (that's dot-matrix-broll), and NOT for AI-generated footage — that's the seedance skill; this one is for the branded typographic beat system (dark navy + electric blue + gold ignite). Keeps every video's b-roll in one consistent, ownable visual language instead of re-deriving the style per video.
---

Turn a video script into branded Machine Editorial b-roll: mobile-first chains of full-screen beats with huge type, a continuous camera, impact physics, and a synth SFX bed — rendered with Remotion from the studio project.

## When to invoke

- User hands over a script / teleprompter text and wants the "slides b-roll" built
- User says "make b-roll slides for X", "motion graphics in our style", "cut this chunk"
- User wants a new segment added to an existing set, or a theme/vertical variant

Skip for: AI-generated _footage_ (people, scenes, camera moves → `seedance`), static slide images, one-off image assets (→ `image-craft-expert` on gpt-image-2, the only image rail for Zalo's brand).

## The design system (Machine Editorial v3)

The b-roll behaves like an intelligent system composing a document in real time — the opposite of bouncy influencer kinetic-type. Implementation lives in `/Users/zalo/dev/operator-broll/src/system/`. Non-negotiable laws:

0. **DEPICTION LAW (governs everything below).** Every animation depicts the idea being spoken at that moment — never "random stuff that looks cool." The mute test: watching the beat with no audio, a viewer should still get what this beat is ABOUT (a cost colliding, three steps assembling, a false fix being killed, a machine waking up). Motion is meaning: things that die get slashed, things that grow roll up, things that assemble fly in, the turn in a story is a literal turn in the path. Craft (gloss, camera, grain) sets the QUALITY; the script line sets the SUBJECT. If you can't name which script phrase a visual depicts, it doesn't go on screen.
   **Corollary — OBJECTS FOR NOUNS, TYPE FOR VERDICTS.** When the script names a THING (a call, a follow-up, a proposal, a link, a person, the AI, money), put the thing on screen as a drawn object from `src/system/objects.tsx` — not as a word. Typography carries verdicts, questions, and payoff lines ("gone.", "turn it on.", "$30,000"); objects carry every noun. A segment that is only type is a red flag (Zalo, 2026-07-09: "is only text though, we need more visuals").
1. **Mobile-first beats.** A segment is a chain of ~2s full-screen beats (`Beat`), max ~5 words on screen at once, key type 150–210px, center-weighted. Never compose a dense slide.
2. **Two speeds only.** Machine-time (linear: line draws, typing, counters) and settle-time (expoOut entrances). Nothing bounces, ever. Camera shake is a decaying mechanical kick, not a spring.
3. **Motion never stops.** One continuous `SegmentCamera` move (push + drift) across the whole segment — no per-beat resets (`Beat push={false}`); beats overlap on enter/exit; grain is live (per-frame).
4. **The Line is a strike-through only.** operator-broll retired it as decoration (Zalo, 2026-07-27; `~/dev/operator-broll/.claude/CLAUDE.md`): a `LineRig` line is allowed for exactly one purpose, the red slash through text you are negating. No travelling underlines, payoff underscores, settles under a headline or divider rules; run `node scripts/check-lines.mjs` before any render. Red only destroys; blue is the machine; gold = money/payoff (max one Ignite per segment); green confirms.
5. **The world reacts.** When a message lands: `ImpactFlash` (2-frame flash + shockwave ring) + the same frame in `SegmentCamera impacts` (camera kick) + SFX hit. Pain beats get no gold and softer impacts.
6. **Typographic craft.** Big words enter via `LetterStamp` (per-letter cascade, tracking tightens on settle, 5-frame chromatic split). Soft middle beats use `WordCascade`. Mono labels via `MonoTag`/`TypeOn`.
7. **Chrome:** `chrome="minimal"` (kicker + floor tag only) for ad cuts and anything mobile; `chrome="full"` (segment ID + live timecode + meta) for full-frame YouTube masters.

### System toolbox (`src/system/`)

| File          | Exports                                                                                                                         | Use                                                                                                                                                                                                     |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `beats.tsx`   | `Beat`, `PushIn`, `WordCascade`, `LetterStamp`, `DimAt`, `TheLine`, `MonoTag`                                                   | beat chains + type craft                                                                                                                                                                                |
| `camera.tsx`  | `SegmentCamera`                                                                                                                 | continuous move + impact shake                                                                                                                                                                          |
| `linerig.tsx` | `LineRig`, `LineKeyframe`                                                                                                       | The Line choreography (comp-level, absolute frames)                                                                                                                                                     |
| `impact.tsx`  | `ImpactFlash`                                                                                                                   | flash + shockwave on landings                                                                                                                                                                           |
| `moves.tsx`   | `Stamp`, `Rise`, `TypeOn`, `Ignite`, `Strike`, `TraceArrow`, `RollUp`, `GoldPill`, `CheckBadge`, `ProgressCircle`               | v1 slide-mode moves (still used inside beats: Stamp/Ignite/RollUp)                                                                                                                                      |
| `tokens.ts`   | `OPERATOR`, `VIOLET`, `TEAL`, `Theme`                                                                                           | themes — variants change accent/grid/bg only; gold/red/green are brand constants                                                                                                                        |
| `Stage.tsx`   | `Stage`                                                                                                                         | void → grid → orb → content → chrome → live grain → vignette; `transparent` for alpha overlays                                                                                                          |
| `world.tsx`   | `World`, `WorldCam`, `Station`, `WorldPath`                                                                                     | infinite-canvas mode: keyframed camera over a huge canvas; path = The Line at map scale                                                                                                                 |
| `objects.tsx` | `UICard`, `CallCard`, `ActiveCallCard`, `ThreadCard`, `DocCard`, `BubbleCard`, `AvatarChip`, `MiniOrb`, `CallPill`, `MoneyChip` | depiction objects — the script's NOUNS drawn in code (calls, threads, docs, bubbles, people, the orb, jobs, money); theme-tokened, frame-deterministic; extend this file when a script names a new noun |
| `plates.tsx`  | `ImagePlate`, `ExhibitFrame`                                                                                                    | AI-image plates (masked RGBA) + documentary exhibit framing                                                                                                                                             |
| `three3d.tsx` | `Scene3D`, `CameraPose`, `mulberry32`, `useGlowTexture`                                                                         | Three.js layer (@remotion/three): real 3D depth/particles UNDER the 2D type system                                                                                                                      |

### Canonical comps (`src/comps/`)

Several of these predate the 2026-07-27 Line retirement and still use the Line for more than a red strike (row rules, a payoff underline, a blue return, a clock line, a settle, a pointer). Copy their structure and timing, never those Line moves; `node scripts/check-lines.mjs` lists the comps whose Line never strikes.

- **`Ch2SellingDiesV3`** — OBJECT KILL-CONVEYOR: the grinds of selling appear as their real artifacts (incoming-call card, no-reply thread, proposal doc, "let me think about it" bubble), each slashed in a fixed center rhythm (in at from+2, slashed at from+20, dur 42); the Line stops for "stop.", returns blue for the verdict. Copy this when the VO lists things that die.
- **`Ch2TheMathV3`** — OBJECT LEDGER: person-with-price rows (AvatarChip + RollUp, Line rules each row at one fixed y), the orb catching CallPills mid-segment ("3:02 AM · STILL WORKING" = never sleeps), MoneyChips converging into the one gold number. Copy this for any cost-vs-payoff math.
- **`Ch2TheLinkV3`** — UI-OBJECT swap: live-call screen (running timer) slashed → URL chip claimed blue → toggle switch flips ON, ● LIVE. Copy this for "the product replaces you / turn it on" beats.
- **`Ch1FalseFixesV3`** — COLLISION flagship: LineRig slashes 3 false fixes red → turns blue → underlines the gold payoff. Copy this for anything old-way-vs-new-way.
- **`Ch1SundayNightV3`** — VERDICT: moody orb cold-open, line as a quiet clock, returns for the verdict. No red, no gold.
- **`Ch1TheMachineV3`** — SYSTEM+COUNT: line tours the step stations, settles under the `RollUp` money pill.
- **`Ch1OneChoiceV3`** — JOURNEY (infinite canvas): story ideas live as `Station`s on one huge canvas; `WorldPath` draws ahead, the camera follows, the path physically turns at the pivot line, destination ignites, zoom-out finale reveals the whole journey.
- **`ExhibitMachineV3`** — EXHIBIT (documentary + AI plate): a gpt-image-2 asset as case-file evidence — `ExhibitFrame` corner ticks, Ken Burns push, LineRig pointing at the detail, mono dossier annotations typing.
- v1 slide-mode comps (`CostCollision`, `ThreeSteps`, `ProofCard`) remain for dense desktop-only explainers (e.g. testimonial receipt cards).

### Mode selection

| Script shape                                               | Mode                | Base comp                   |
| ---------------------------------------------------------- | ------------------- | --------------------------- |
| Sequential punches (kill list, steps, money)               | Beat chain          | FalseFixesV3 / TheMachineV3 |
| Emotional hold / cold open                                 | Verdict             | SundayNightV3               |
| A story with a turn ("they did X, then Y, and it changed") | Infinite canvas     | Ch1OneChoiceV3              |
| Evidence, proof, product/object showcase, "look at this"   | Documentary exhibit | ExhibitMachineV3            |
| Dense receipt (testimonial with badges) for desktop        | v1 slide mode       | ProofCard                   |

## Infinite-canvas mode (JOURNEY)

For story-shaped passages: lay ideas out spatially, fly the camera between them, reveal the journey at the end. Rules:

- Canvas ~6000×3000; `WorldCam` keyframes = hold → hop (30–35f, cubic in/out — filmed, never springy) → hold. Everything inside `World` uses ABSOLUTE frames and CANVAS coordinates; chrome stays outside.
- `WorldPath` progress keyframes are cumulative length fractions — the head must arrive at each station ~4f BEFORE the camera does (the Line leads, the camera follows).
- Station text: ≤6 words, 105–140px; entrances via WordCascade/LetterStamp timed to camera arrival.
- End the path AS the underline of the destination station; make the path physically turn where the script turns.
- Zoom-out finale: check the whole-canvas bbox fits the viewport at finale z (text half-widths included) — clipped words are the classic bug.

## AI image plates (gpt-image-2)

When a beat benefits from an object/scene (product core, mockup, metaphor), generate a masked plate and composite it:

1. Generate on chroma green (gpt-image-2 rejects `background:"transparent"`): prompt must say "COMPLETELY ISOLATED on a solid uniform pure chroma-key green background (#00FF00), no floor, no ground shadow, no reflections". Use `quality:"medium"` (high often drops the connection) via `curl https://api.openai.com/v1/images/generations` with `$OPENAI_API_KEY`. Never nano-banana/Gemini: gpt-image-2 is the standing image rail.
2. Key + trim: `ffmpeg -i in.png -vf "colorkey=0x00FF00:0.32:0.08,despill=type=green" -frames:v 1 out.png`, then PIL `getchannel('A').getbbox()` crop. Save to `public/assets/`.
3. Composite with `<ImagePlate src="assets/x.png" width at over kenBurns sweepAt/>` — entrance settle, continuous Ken Burns, ambient accent glow, optional documentary highlight sweep. Wrap in `<ExhibitFrame label="fig. 01 — ...">` for the documentary look.
4. Style guardrail: ask for dark-navy + electric-blue palette, premium 3D render, rim light — plates must sit in the Stage's world, not on top of it.

## 3D layer (Three.js under the type)

For beats that earn real depth — cold opens, data visualizations, payoff atmospheres — put a Three.js world UNDER the 2D system, never instead of it. Typography, The Line, chrome, grain stay 2D on top (3D text is always a downgrade); the scene provides parallax, particles, volume. Pilot: `TokenField3D` (4,000 instanced tokens; camera flies through; they assemble into the context-window slab as the term stamps in 2D).

Rules:

- `Scene3D` inside a normal `Stage` (`orb={false}`); scene background/fog use theme void colors so grain/vignette blend.
- DETERMINISM IS LAW: all motion pure functions of `useCurrentFrame`; `mulberry32(seed)` for any randomness — `Math.random`/`useFrame` deltas break renders.
- Camera pose = pure `(frame) => {pos, look, fov}`; impacts = the same decaying SHAKE pattern applied to the pose.
- InstancedMesh for particle counts (matrix updates in a `useLayoutEffect` keyed on frame); additive-blended `useGlowTexture` sprites for atmosphere; no post-processing dependency needed — emissive colors + 2D grain/vignette carry the look.
- Overlay text over busy 3D gets `textShadow: '0 2px 30px rgba(2,6,13,0.95), 0 0 60px rgba(2,6,13,0.8)'`.
- Render/still with `--gl=angle`. `@remotion/three` version must EXACTLY match the remotion version.
- The 3D subject must BE what the script line talks about (Depiction Law): tokens for "tokens", the orb for the machine's presence, panels for "install". 3D that's merely atmospheric while the VO makes a specific claim = decoration; cut it or make it literal.
- 3D archetype boilerplates: PARTICLE-DATA (built: TokenField3D), GLOSS (built: GlossClump3D — look-dev), ORB-SCENE (built: OrbScene3D — the production template for hero beats), TUNNEL-JOURNEY (canvas mode in true z), OBJECT-SHOWCASE (GLB turntable).
- GLOSS is a QUALITY TIER, not a subject: apply the material/lighting/negative-space recipe to BRAND-MEANINGFUL objects (the orb, the machine core, panels, steps) — never generic balls/confetti for production. OrbScene3D is the reference application: one opaque dark-clearcoat mass (`#0E2242`, roughness 0.26, clearcoat 1) + additive `useGlowTexture` halo sprite behind it + one blue rim pointLight.
- GOTCHA: `meshPhysicalMaterial transmission` (glass interiors) renders black/hollow under headless `--gl=angle` — do NOT use transmission; fake glass depth with opaque clearcoat + halo + rim light.

### GLOSS — the Apple-clean look (default 3D quality tier for hero/brand beats)

`GlossClump3D` is material/lighting look-dev ONLY (abstract spheres fail the Depiction Law — never ship its subject). Copy its recipe onto objects the script is actually about (`OrbScene3D` is the shipped example). Few LARGE objects, never confetti; premium comes from physically-based light, not motion complexity:

- `Scene3D environment="room"` — offline PMREM RoomEnvironment gives the studio window-reflections that make surfaces read expensive. `fog={null}` (fog kills gloss).
- Material recipe: `meshPhysicalMaterial` `roughness≈0.22 metalness=0 clearcoat=1 clearcoatRoughness≈0.12 envMapIntensity≈1.15`; sphereGeometry 64 segments.
- Palette by count for ~16 objects: ~6 deep navy, ~5 accent blue, ~4 white, exactly ONE gold (the payoff accent, same law as Ignite).
- Long lens (fov 30–35) + NEGATIVE SPACE: object cluster ≤ ~55% of frame height, floats in void, camera dollies slowly. Filling the frame is the failure mode.
- Clump layout: seeded scatter + fixed-iteration overlap-relaxation in `useMemo` (deterministic physics-look without a physics engine); staggered fly-ins (i\*4 frames, 40f expoOut); group rotates ~0.004 rad/frame; per-object breathe ≤0.006.
- Type stays 2D, enters soft (no stomp over serene 3D), with the standard text-shadow.

## Documentary mode (EXHIBIT)

For proof/evidence/product beats: single continuous scene (no beat swaps — documentary holds), asymmetric layout (dossier column left, evidence right), `MonoTag` exhibit numbers, `TypeOn` annotations (caps mono, faint) placed beside the detail each one names (no LineRig pointer: the Line only strikes), Ken Burns on the plate, one highlight sweep. Screenshots/photos get the same treatment as AI plates (key/crop → `public/assets/`).

## Archetype roadmap (recipes for scenarios not yet built)

When a script needs one of these, build it from the named ingredients — do NOT invent a new visual language:

| Scenario                                      | Recipe (system ingredients)                                                                                                             |
| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| DATA / CHART — a stat that matters            | bars/lines draw in machine-time (SVG pathLength like ProofCard border), `RollUp` values; one gold delta    |
| VS TABLE — us vs them                         | two columns, rows land as beats; theirs gets `Strike`, ours gets `CheckBadge`; never both visible before their beat                     |
| TIMELINE / ROADMAP — day 1 / 30 / 90          | horizontal canvas-mode variant: stations on a dated axis, WorldPath IS the timeline                                                     |
| STACK / OFFER — "everything you get"          | items stamp one per beat with gold FREE pills (v1 slide-25 style, beat-ified); ends in CTA card                                         |
| CTA END-CARD — every video's last 5s          | gold pill button + `TypeOn` URL; impact + shimmer; keep ONE variant reused everywhere                       |
| DEFINITION — introducing a term ("Operator")  | dictionary beat: word huge (LetterStamp), mono phonetics, definition types on                                 |
| HOOK / QUESTION — open loops                  | Verdict-mode variant with the question igniting nothing — withhold gold until the answer segment                                        |
| MAP / GEO — "operators in 40+ countries"      | dark dotted map plate + green `CheckBadge` pings (confirmation color)                                                                   |
| CHAPTER DIVIDER — YouTube retention structure | 2s beat: mono `01`, chapter title LetterStamp; chrome='full'                                                                |
| LOWER-THIRD / OVERLAY — over talking head     | `transparent:true` ProRes comps: name tag, one-liner captions; keep to bottom third                                |
| UI DEMO — product walkthrough                 | screen recording via `<OffthreadVideo>` inside a browser/phone frame (slide-13 style), mono labels typing beside the features |

## Voice & judgment (the taste layer)

- Every segment: ONE hero moment (an ignite, a slash, a reveal, a number). If a segment has two, split it.
- **The stomp is hero-only.** `LetterStamp aberrate` (chromatic split) + ImpactFlash + thock together = the stomp; reserve it for the segment's verdict/payoff moment — one, maybe two per segment. Supporting lines enter soft: `WordCascade`, `Rise`, or plain `LetterStamp` (aberrate defaults to false). If every line stomps, nothing does.
- Pain gets no color. Money always rolls. Red never emphasizes — it kills.
- **Run the mute test on every beat** before rendering: with the audio off, does the motion still say what the VO says? If the answer is "it looks premium but says nothing," the beat fails the Depiction Law — replace the visual with one that acts out the script phrase.
- When in doubt, remove words and enlarge what remains.
- **Reading time: fast in, then hold** (from latent-spaces/brag `step-2-plan.md`). Every line the viewer must read stays settled (fully in, not yet exiting) for at least 0.8 s if it is a label of 1 to 3 words, and 0.3 s per word (1.2 s minimum) if it is a headline or sentence. Pace comes from snappy entrances and cuts, never from pulling text early. This fits the ~2 s beats and the 5-word ceiling: 5 words need 1.5 s, which is inside one beat. A line struck through while it stays on screen still counts as readable.

## How to use

1. **Read the script**, split into segments (one idea cluster each, 7–9s), then each segment into beats (~2s, one idea). Per beat, write the script phrase it covers and name what the visual DEPICTS from that phrase (Depiction Law — the mute test) before picking any move. Per segment decide: which archetype comp to copy, the ONE gold payoff (if any), what dies in red, what number rolls.
2. **Place the strike-throughs.** The Line only slashes text being negated (red). Write each strike as `LineKeyframe[]` in ABSOLUTE comp frames: born, slash (draw about 8 to 12f), hold, release (collapse w→0, `o:0`). No travel, no underline, no final settle. Beat-internal `at` values (LetterStamp/DimAt/SFX) are SEQUENCE-RELATIVE — keep a beat map comment reconciling both.
3. **Beats on a 6-frame overlap grid**: `from` = previous from + duration − 6; final beat gets `exit={0}` and ends exactly at `durationInFrames`.
4. **Impacts + SFX on landing frames**: `SegmentCamera impacts={[...]}` = `ImpactFlash at` frames = SFX hit frames. The 4 role sounds live in `public/sfx/` (thock=stamp, slash=kill, riser=into payoff, shimmer=ignite). Place them via `<Sequence from><Audio src={staticFile(...)} volume={0.4-0.9}/></Sequence>`. See "Sound effects (Kenney CC0 library)" below for what each file is and where its peak sits.
5. **Register** in `src/Root.tsx` (1920×1080@30; 200–280 frames/segment), then verify cheaply before committing to renders:

```bash
cd /Users/zalo/dev/operator-broll
npx tsc --noEmit
npx remotion still <CompId> out/preview.png --frame=<slashOrPayoffFrame>   # inspect with Read
npx remotion render <CompId> out/<CompId>.mp4          # 1080p h264 + aac (SFX included)
# filmstrip to check motion arc:
ffmpeg -y -i out/<CompId>.mp4 -vf "select='not(mod(n,38))',scale=620:-1,tile=4x2" -frames:v 1 out/strip.png
```

6. **Delivery formats:**

```bash
# 4K upscale for YouTube masters
npx remotion render <CompId> out/<CompId>-4k.mp4 --scale=2
# Transparent overlay (graphics OVER talking head): transparent:true drops void/grid/orb/grain/vignette
npx remotion render <CompId> out/<CompId>.mov --codec=prores --prores-profile=4444 --image-format=png --pixel-format=yuva444p10le --props='{"transparent":true}'
# Verify alpha: ffprobe shows pix_fmt=yuva444p… (the "a" = alpha).
# Never set Config.setProResProfile in remotion.config.ts — it is global and breaks all h264 renders.
# Vertical 9:16: register a second Composition (1080x1920) on the same component with defaultProps={{scale: ~0.78}}
```

7. **Poster as frame 0 (every delivered reel, teaser and launch cut).** X, Slack, Discord and most players use frame 0 as the idle thumbnail and ignore cover-art metadata. A comp that opens on black or mid-animation therefore posts as a black box (the Leash 30 s opened at 8 of 255). After the final mix, bake a settled frame into frame 0. Only frame 0 changes: the audio is copied, and the duration and frame count stay the same. The script refuses to overwrite its input and runs its own checks, exiting 1 on any failure.

```bash
cd /Users/zalo/dev/operator-broll
scripts/poster-frame0.sh <final.mp4> <final>-poster.mp4 --at <settled-seconds>   # or --image <poster.png>
# prints: size/fps/frames/duration equal, audio packets md5 equal, frame 0 vs poster PSNR,
# frames 1+ vs input min PSNR (>= 40 dB); RESULT: PASS. Also writes <final>-poster.poster.png,
# the custom thumbnail for platforms that take an upload (YouTube, IG, TikTok, LinkedIn).
# Proof mode: --lossless makes frames 1+ hash identical to the input (never deliver it: many phones cannot play it)
```

Pick the hook line, the hero reveal or the final logo at a SETTLED moment: text fully in, before it exits. Deliver the `-poster` file and never overwrite the original.

## Beat sync (music-driven cuts)

When the cut rides a music bed rather than a voiceover, measure the track and snap the reveals to it. Do not guess from the BPM.

```bash
cd /Users/zalo/dev/operator-broll
tools/audio/beats.sh public/music/<track>.mp3 --md public/music/<track>.beats.md   # writes public/music/<track>.beats.json
# first run builds tools/audio/.venv (uv, arm64 CPython 3.12, pinned lock; never the system python3)
node --experimental-strip-types --no-warnings --test tools/audio/beatSync.test.ts  # helper tests
node --experimental-strip-types --no-warnings tools/audio/snap-report.ts          # BeatLab plan: planned vs snapped vs nearest cue/beat
```

In the comp (`src/system/beatSync.ts`; the worked example is `src/beatlab/`, rendered through its own entry `src/beatlab.ts`):

```tsx
// Composition: calculateMetadata={async () => ({ props: { beatMap: await loadBeatMap(staticFile("music/x.beats.json")) } })}
const map = placeTrack(beatMap, { trimStart: 58, until: 11 });   // same trim as <Audio trimBefore={58 * 30}>
const { frames, warnings } = planReveals(map, [
  { kind: "major", label: "payoff", at: 8.5 },                                   // strong cue within 0.15 s
  { kind: "sequence", labels: ["a", "b", "c"], at: 4.0, readableText: true },    // consecutive beats within 0.10 s
], 30);
// frames.payoff is the LANDING frame (flash, SFX, stamp settle); subtract the move's own lead-in for `at`/`from`
```

The rules the helper enforces (adapted from latent-spaces/brag, MIT):
- Land 1 to 3 major reveals per video within 0.15 s of a strong cue. A reveal moves at most 0.5 s (`reachS`) to reach one; with no cue in reach it keeps its planned time and says so in `warnings`.
- Items that appear one after another go on consecutive beats, each within 0.10 s of its beat.
- Above 110 BPM, readable text takes every other beat (`readableText: true`). Accents (flashes, dots, ticks) may hit every beat.
- Readability and the story win. A VO-timed segment stays on the VO; beat sync is for music-led reels, teasers and launch cuts.

## Sound effects (Kenney CC0 library)

The role files in `public/sfx/` are CC0 Kenney sounds. Each one is level matched to the ffmpeg placeholder it replaced (same active RMS, peak capped at -1 dBFS), so existing mixes keep their balance. The placeholders are kept as `public/sfx/<role>.placeholder.wav` for A/B comparison.

| Role file | Source (`public/sfx/kenney/`) | Peak lands | Place it |
|---|---|---|---|
| `thock.wav` (stamp) | `impact-sounds/impactSoft_medium_000.ogg`: warm, low HF risk | 11 ms | `from` = landing frame |
| `slash.wav` (kill) | `rpg-audio/knifeSlice.ogg`: a real blade swish, bright (HF risk high) | 165 ms | `from` = strike completes minus 5 frames; keep repeated slashes at volume 0.5 or below |
| `riser.wav` (into payoff) | `interface-sounds/maximize_005.ogg`: rising sweep, padded to peak where the old riser peaked | 885 ms | `from` = payoff frame minus 27 |
| `shimmer.wav` (ignite) | `interface-sounds/confirmation_002.ogg`: tonal rising chime, low HF risk | 11 ms | `from` = ignite frame |

- The library holds 521 rated files from 7 packs. `manifest.json` has per-file ratings (brightness, high-frequency risk, envelope, features), the top candidates per role, and the picks with the reason for each choice. `RATINGS.md` is the readable summary, `LICENSE.md` the CC0 note, `sources.json` the pack URLs and zip hashes.
- To use a different library sound, pick by `labels.highFrequencyRisk`. Use low for anything repeated; keep high for isolated hits at low volume. Then install it with `tools/audio/.venv/bin/python tools/audio/install_sfx_picks.py --pick <role>=<pack>/<file>.ogg --why "<role>=<reason>"`, which keeps the placeholder and level matches. Re-rate after adding packs with `tools/audio/rate_sfx.py` (see its header).
- These role levels were tuned for b-roll under a VO. Under a music bed the riser and shimmer are quiet, so lift them (volume 1.5 to 2.5 is valid in a render) and loudness-check the mix.

## Output shape

Report per segment: `✓ <CompId> — <duration>s → out/<CompId>.mp4` plus one inspected still per new comp. On render failure: comp ID + last 15 log lines, never the full log.

## Anti-patterns

- ❌ Decoration without depiction — a visual chosen because it "looks cool" rather than because it acts out the script phrase (the GlossClump lesson: 16 beautiful spheres that depicted nothing). Fails the mute test → doesn't ship
- ❌ Type-only segments — if every beat is words, the nouns weren't depicted; pull the things the VO names out of `objects.tsx` (or add the missing object there) and reserve type for verdicts/payoffs
- ❌ Bouncy/elastic easing, overshoot, springs — the look this system exists against
- ❌ Slide-density in a beat: >5 words simultaneously, lists, side-by-side columns on mobile cuts
- ❌ The Line as decoration (underline, travel, settle, divider): it is a red strike-through only; `check-lines.mjs` lists every comp whose Line never strikes, so a comp that strikes once and also underlines still passes it
- ❌ Mixing frame spaces: LineRig/ImpactFlash/camera impacts are ABSOLUTE; LetterStamp/DimAt/SFX-in-beat are RELATIVE to their Sequence
- ❌ More than one Ignite per segment; gold on non-payoff words; red for emphasis (red only destroys)
- ❌ Impact without reaction — every flash frame must also appear in `SegmentCamera impacts` and carry an SFX hit
- ❌ Rendering the full MP4 before inspecting a still
- ❌ TypeScript 7 in the studio — Remotion's bundler breaks; stay on typescript@5.x

## Edge cases

- **Long holds for VO** — extend the final beat; camera drift + live grain + orb breathing keep it alive.
- **New brand/series** — clone a theme in `tokens.ts` (accent/grid/bg only).
- **Music-synced cuts**: measure the track with `tools/audio/beats.sh` and snap with `src/system/beatSync.ts` (see "Beat sync"). A BPM gives the spacing, not where the beats fall or which ones are strong, so do not quantize to 1800/BPM by hand.
- **Real SFX**: done 2026-09-26. The role files are Kenney CC0 picks and the placeholders are kept as `*.placeholder.wav` (see "Sound effects").

## Pair with

- `seedance` — AI footage clips to intercut with these graphic segments
- `view-video` — frame-extract a rendered MP4 to verify motion
- `transcribe` — pull timing from a recorded teleprompter take to size segments
- `commit-with-heredoc` — commit studio changes after renders verify

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…