use-screenkite-advanced-b-roll
Purpose
Layer short, animated B-roll visuals on top of a ScreenKite recording's screen+camera composition. The screen recording stays the main content; B-roll is a brief accent (4–6s corner PiP) that appears, lets the viewer read it, then disappears via magicMove.
Pipeline (7 phases)
1. Locate mic audio in .skbundle
2. Transcribe with ElevenLabs Scribe → word-level JSON
3. Pack to phrase view + proofread via web search
4. Propose visual idea menu with density bundles
5. Scaffold N Hyperframes projects (one per visual slot)
6. Dispatch parallel sub-agents → each writes one index.html
7. Render each to MP4 → apply setSceneLayout DSL with magicMove
Prerequisites (check before starting)
- ScreenKite.app at
/Applications/ScreenKite.app(macOS). - Node ≥ 22 and FFmpeg on PATH.
- uv (recommended) — fast Python script runner. If available, prefer
uv runoverpython3— it auto-provisions dependencies via PEP 723 inline metadata (no manualpip installneeded). Install:curl -LsSf https://astral.sh/uv/install.sh | sh. Falls back topython3ifuvis not installed (you'll need topip install requestsmanually fortranscribe_mic.py). ELEVEN_LABS_API_KEYin the project.env(note: notELEVENLABS_API_KEY— this repo uses the underscored form). If the key is missing or.envdoes not exist, ask the user to provide the key before proceeding. Do not silently fail or skip transcription.- Hyperframes CLI — invoked via
npx hyperframes(auto-installs on first use). Repo: https://github.com/heygen-com/hyperframes - Hyperframes agent skills (optional but helpful for sub-agents):
npx skills add heygen-com/hyperframes— registershyperframes,hyperframes-cli,gsapskills. - video-use helpers (bundled copies at
scripts/transcribe_mic.pyandscripts/pack_transcripts.py; upstream: https://github.com/browser-use/video-use).
If anything is missing, instruct the user to install it before proceeding. See references/external-tools.md.
Phase 1 — Locate mic audio
A ScreenKite .skbundle is a directory. Mic audio lives at <bundle>/media/microphone_*.m4a. Screen at <bundle>/media/screen_*.mp4, camera at <bundle>/media/camera_*.mov, system audio at <bundle>/media/system_audio_*.m4a.
Confirm the project is open in ScreenKite (so later CLI calls have a current project):
'/Applications/ScreenKite.app/Contents/MacOS/ScreenKite' agent project open --path '<bundle path>' --json
'/Applications/ScreenKite.app/Contents/MacOS/ScreenKite' agent project current --json
Phase 2 — Transcribe
Use scripts/transcribe_mic.py — a thin wrapper over ElevenLabs Scribe that reads ELEVEN_LABS_API_KEY from the project .env and caches per-source. Prefer uv run (auto-installs requests via PEP 723 inline metadata); python3 works if requests is already installed.
Before running: verify ELEVEN_LABS_API_KEY exists in the project .env. If it does not, stop and ask the user for the key. Write it to .env as ELEVEN_LABS_API_KEY=<key> before proceeding.
uv run scripts/transcribe_mic.py \
'<bundle>/media/microphone_dji-mic-mini.m4a' \
--edit-dir '.' \
--language zho \
--num-speakers 1
--edit-dir defaults to the current workspace directory (.) so transcripts, packed views, and B-roll all live alongside the agent's working tree. You can also pass --edit-dir '<bundle-parent>/<project-slug>-edit' to co-locate outputs next to the .skbundle instead.
Output: <edit-dir>/transcripts/microphone_*.json (word-level, diarized, audio events, ~155KB for 3 minutes).
Cached: skips re-upload if output exists.
Phase 3 — Pack and proofread
3a. Pack to phrase-level view
uv run scripts/pack_transcripts.py --edit-dir '<edit-dir>'
Produces <edit-dir>/takes_packed.md — phrases break on silences ≥ 0.5s. 10× more compact than raw JSON.
3b. Proofread — MANDATORY for any English product/proper nouns
ASR often misrecognizes proper nouns when the speaker mixes languages. In one derived session, Scribe consistently heard "Claude" as "Cloud" throughout a Chinese recording. Every such misrecognition propagates into the generated visuals.
Rule: Before Phase 4, scan the packed transcript for any English word that looks like a product name. For each one, use WebSearch to verify:
- Is this a real product with that exact spelling?
- Is the context consistent (launch date, capabilities, positioning)?
- What is the canonical capitalization?
Write the corrected version to <edit-dir>/takes_packed_proofread.md with a small diff table showing what was corrected and why.
Do NOT skip this step, even if it looks obvious. Seven visuals with the wrong product name waste 15+ minutes of render time.
Phase 4 — Visual idea menu + density
Propose the menu. Read
references/visual-ideas-menu.mdfor a long list of beat-appropriate visuals. Propose a menu organized by beat (intro / tool-name / analogy / feature-highlight / pricing / outro etc.).Offer density bundles:
| Density | Count | Pace | When |
|---|---|---|---|
| Sparse | 4 | ~40s apart | Documentary feel |
| Medium | 7 | ~24s apart | Default — balanced |
| Dense | 11–13 | ~13s apart | Explainer energy |
| Hyper | 15+ | phrase-level | Short-form retention-max |
Default to Medium (7) unless the user asks otherwise. B-roll is an accent on a screen-recording tutorial, not the main act.
- Wait for confirmation. Do not scaffold until the user approves a bundle.
Phase 5 — Scaffold N Hyperframes projects
One Hyperframes project per slot, all under <video-project-root>/broll/slot_XX/:
broll/
├── slot_01/
│ ├── hyperframes.json
│ ├── index.html (written in Phase 6)
│ └── assets/ (logos, images referenced by index.html)
├── slot_02/ ... slot_NN/
Use uv run scripts/scaffold_slots.py <broll-dir> <count> to create the directories with hyperframes.json stubs. Copy logo/asset files into the appropriate slot_XX/assets/ folder if a slot needs them (e.g., ScreenKite self-promo).
Phase 6 — Parallel sub-agents build each index.html
Dispatch N sub-agents in a single message (each Agent tool call inside one response = parallel). Each agent brief must be fully self-contained. Use references/subagent-brief-template.md as the template — it encodes every hard rule below.
Hard rules every brief must carry
See references/hyperframes-contract.md for the full list. The critical ones:
Content fills the full 1920×1080 MP4 frame. The MP4 IS the PiP — when ScreenKite places it with
placeSelf="topRight" width="40%", the whole frame lands in the corner. Do not put content in a sub-corner of the MP4 (it'll be buried in a corner-of-a-corner). Warm cream full-bleed background, content centered and sized to fill.Timeline shape: entry → settled hold → NO internal exit. ScreenKite's
magicMovehandles the exit. If the composition has its own exit animation, viewers see a double-exit. Structure:0.0 – 1.5s: entry animations (pop, slide, bounce)1.5 – end: settled hold — visual is fully readable, minimal ambient motion- Do NOT add a fade/scale-out in the last 0.5s.
Composition duration ≥ DSL window duration + 1.5–2s reading hold. The viewer needs time to read after the entry finishes. A 5s DSL window needs at least 3.5s of post-entry hold. Don't make visuals flash by.
Synchronous timeline, paused, registered at
window.__timelines["main"]. Noasync,setTimeout,Promise. NoMath.random,Date.now,repeat: -1. Finite repeat counts only:repeat: Math.ceil(holdLen / cycleLen) - 1.Standalone composition (no
<template>wrapper). Root div directly in<body>. Seereferences/dsl-cookbook.mdfor the boilerplate.Lint before reporting done. Each agent runs
npx hyperframes lint --jsonfrom the slot directory, fixes errors, reports warnings.
Launching sub-agents
Launch in ONE message with N Agent tool calls, all run_in_background: true. Each prompt:
- Absolute output path:
<broll>/slot_XX/index.html - Slot content spec (what to show, what text, what icons)
- Palette (hex values — no "choose a color")
- Font sizes (LARGE — display 160-220px, body 48-72px)
- Duration and timeline beats
- Asset paths if applicable (logos, images)
- The hard rules above
- "Do NOT ask questions. If ambiguous, pick the most obvious warm/cute interpretation."
Phase 7 — Render + apply DSL
7a. Render serially
Each npx hyperframes render spawns Chrome. Running in parallel causes contention. Serial is fine — a 30s composition renders in 30–50s:
for i in $(seq -w 01 07); do
( cd broll/slot_$i && npx hyperframes render --output renders/slot_$i.mp4 --quality standard )
done
Quality: use standard for review, high for final delivery, draft for fast iteration.
7b. Plan DSL windows (brief + reading hold)
Default placement rotation for N=7:
- Slot 1:
bottomLeft 40%(lower-third), 3.6s - Slots 2, 3, 5, 7:
topRight38–45%, 5–5.5s - Slots 4, 6:
topLeft40–42%, 4.5–6s
Camera stays at bottomRight 22% aspect=3:4 across all slots for continuity.
Each slot's display window should be entry + reading hold, not the composition's full duration. If a composition is 12s long (entry 1.5s + long hold), a 5s display window shows entry + 3.5s hold, then magicMove takes over.
7c. Apply setSceneLayout
Use scripts/apply_broll_dsl.py — reads a plan JSON, applies each via the ScreenKite CLI, then clears any old tails.
uv run scripts/apply_broll_dsl.py <plan.json>
See references/dsl-cookbook.md for the plan.json schema and ready-to-copy DSL snippets.
7d. The tail-clearing gotcha
setSceneLayout splits existing segments; it does not replace. If you previously applied a 22-second advanced segment and now re-apply a 5-second one at the same start, you end up with a new 5s segment + a 17s leftover tail still showing advanced mode. The tails must be cleared by calling setSceneLayout with mode: "pictureInPicture" on each tail range.
apply_broll_dsl.py handles this automatically: after applying new B-roll windows, it reads the layout state, finds any advanced segments NOT matching the intended plan, and reverts them to PiP.
7e. Verify
'/Applications/ScreenKite.app/Contents/MacOS/ScreenKite' agent tool call \
--name getProjectState --input-json '{"scope":"layout"}' --json
Confirm exactly N advanced segments at the intended ranges, with pictureInPicture everywhere else. Open ScreenKite and scrub to visually verify.
Iteration
If a visual is wrong (content, timing, placement):
- Edit
broll/slot_XX/index.html(or regenerate via a targeted sub-agent) - Re-render:
cd broll/slot_XX && npx hyperframes render --output renders/slot_XX.mp4 - Update the plan.json and re-run
apply_broll_dsl.py(it's idempotent — tails get re-cleared)
The ScreenKite DSL references the MP4 by absolute path, so replacing the file is enough for timing-unchanged iterations.
References (load on demand)
references/external-tools.md— installation/repo URLs for every prerequisitereferences/dsl-cookbook.md— DSL snippets per placement + plan.json schemareferences/hyperframes-contract.md— all hard rules for composition authoringreferences/visual-ideas-menu.md— beat-by-beat idea catalog + density bundlesreferences/subagent-brief-template.md— self-contained brief template for parallel agents
Scripts (token-efficient)
Prefer uv run (auto-provisions deps, no venv needed). Falls back to python3 — only transcribe_mic.py needs requests; the rest are stdlib-only.
scripts/transcribe_mic.py— ElevenLabs Scribe wrapper, cached, usesELEVEN_LABS_API_KEYscripts/pack_transcripts.py— phrase-level packing from raw Scribe JSONscripts/scaffold_slots.py— create N slot dirs with hyperframes.json stubsscripts/apply_broll_dsl.py— apply setSceneLayout from plan JSON + clear tailsscripts/find_skbundle_mic.py— locate mic audio inside a .skbundle
Anti-patterns
- Don't skip the proofreading step. Scribe will get English proper nouns wrong in Chinese/ZH-TW recordings; verify before generating visuals that say "Cloud" instead of "Claude".
- Don't position content in a corner of the MP4. The MP4 is the corner. Content fills the frame.
- Don't let a composition exit animate inside its own frame. magicMove handles exit. Internal exits produce double-fades.
- Don't re-apply setSceneLayout without clearing tails. You'll end up with zombie B-roll playing for the full original window.
- Don't render in parallel. Chrome contention. Serial is fast enough.
- Don't re-transcribe cached sources. Immutable input → immutable output. Scribe costs money.
- Don't start rendering before the user confirms density + bundle picks. Re-work is expensive.
- Don't use Remotion here if Hyperframes is already installed. Stick to one tool for this skill.