broll-cursor
Produce the base layer of a technical reel from an animated IDE agent chat
— a Cursor-style "Agent" panel. It's the sibling of broll-terminal:
where that renders a shell (typed commands + streamed stdout), this renders a chat
(a typed user prompt + a streaming assistant turn with tool-call rows). Use it
when the story is "someone asked an agent to do something and it did it" — the exact
register for a set of Cursor skills.
It renders deterministically (HTML/CSS + Playwright, frame by frame) so the
output is crisp and reproducible, and it never runs a real agent or shell — the
assistant only "says"/"does" whatever your session.json declares.
Two-layer architecture (shared with broll-terminal / broll-web-capture):
broll-cursor / broll-terminal / broll-web-capture ← BASE layer (proof visual)
+
talking avatar in PiP ← OVERLAY layer (credential anchor)
When to use
- "Make B-roll of a Cursor / AI agent chat doing this."
- "Show the agent being prompted and streaming the skills/tool-calls."
- "Animated 'this is the request that built the video' opener."
- Anything where the proof visual is an IDE agent conversation (not a terminal).
Use broll-terminal instead when the proof visual is a
command line (typed commands + stdout).
Setup
pip3 install -r .cursor/skills/broll-core/scripts/requirements.txt
pip3 install -r .cursor/skills/broll-cursor/scripts/requirements.txt
playwright install chromium
Requires the broll-core skill alongside this one (shared geometry + the PiP
compositor) and ffmpeg/ffprobe on PATH. Fonts: chat text uses the system UI sans
(-apple-system/Segoe UI/Inter…); tool-call rows and file paths use JetBrains Mono
/ Fira Code if installed, falling back to Menlo (always on macOS) — no download.
Quick start
S=.cursor/skills/broll-cursor/scripts
# 1) Render the bundled example -> 9:16 base clip
python3 $S/make_broll_cursor.py $S/../examples/session.json
# 2) Your own session, a kicker + model pill
python3 $S/make_broll_cursor.py my_session.json \
--kicker "one prompt → one reel" --model "claude-4.6-sonnet"
# 3) Base + avatar PiP overlay (avatar circle, bottom-right)
python3 $S/make_broll_cursor.py my_session.json \
--avatar sherlock/generated-videos/pip_locked.mp4 --layout pip-circle --corner br
Output: a numbered clip under --out-dir (default broll_cursor/) + a
manifest.json that drops into avatar-reel-composer (see Integration).
How it works
timeline.py session JSON -> deterministic event list with absolute-ms timing:
the user prompt typed with human jitter (variable per-key delay, an
occasional typo+backspace), an assistant turn that streams token-by-
token after a believable latency, and tool-call rows that spin then
resolve with a checkmark.
↓ timeline
cursor.html Playwright loads it (high-DPI) and exposes seekTo(t); a themed Cursor-
style window (traffic lights, "Agent" header + model pill, message
transcript with user bubbles / assistant text / tool rows, a composer
with a blinking caret while typing, autoscroll tail).
render_cursor.py drives seekTo(t) once per frame, screenshots each (frame-exact),
muxes the PNG sequence to H.264 at the canonical reel dims.
↓ base clip (silent)
pip_overlay.py (broll-core, optional --avatar) composite the talking avatar in a
PiP circle/split; the avatar's audio drives length, base loops.
↓ final clip + manifest entry
The session JSON
Everything but steps is optional. Steps run in order; the timeline threads them
into one conversation.
{
"theme": "cursor-dark", // cursor-dark | cursor-light
"title": "Cursor — avatar-skills", // window title-bar text
"kicker": "one prompt → one reel", // small uppercase label above the window
"model": "claude-4.6-sonnet", // model pill in the header + composer
"agent_name": "Agent", // header / assistant label
"font_size": 21, // base chat font (logical px)
"placeholder": "Plan, search, build anything…", // composer placeholder
"typing": { "typo_chance": 0.1 }, // per-key jitter (see REFERENCE.md)
"assistant": { "cps": 55 }, // assistant streaming speed (chars/sec)
"steps": [
{ "user": "invent a Sherlock-style detective host for an avatar-skills demo" },
{ "say": "On it — building the presenter with the pipeline." },
{ "tool": "avatar-invent", "detail": "inventing the face", "duration_ms": 1600, "done": "hero + 5 angles" },
{ "say": "Done — your detective is live in {c}avatares/sherlock{/}." }
],
"tail_ms": 1000
}
Step kinds:
user— a prompt typed into the composer key-by-key (human jitter), then "sent" so it becomes a user bubble at the top of the transcript.say— an assistant message that streams in token-by-token. Accepts inline color markup{c}…{/}(andg y b m w r dim bold) for paths/keywords.tool— a tool-call row (a skill):name(mono) +detail(shown while it spins) → resolves to a check +done.duration_mssets the spin time.sleep_ms— a pause.
Keep the user prompt to one short sentence and tool name/done short so rows
don't wrap. A tight beat (one prompt + a few tool rows + a closing line) reads best.
Avatar PiP overlay (--avatar)
Identical compositor (and rules) as broll-terminal, from broll-core:
--layout pip-circle(default): avatar in a corner circle over the near-full chat —--corner br(default) sits over the composer's empty area.--layout split: chat on top, avatar on the bottom.--face-bias 0..1(default0.4): vertical crop of the circle (lower keeps the face).- The avatar clip must be a static, face-forward
pipshot (avatar-camera-angles --move pip) lip-synced locked withavatar-talking-video(p-video-avatar). - The base is silent; the avatar carries the audio and sets the length (the ending hold auto-extends to cover the narration, so the chat never loops mid-clip).
Key options
| Flag | Default | Notes |
|---|---|---|
session |
— | path to the session JSON |
--aspect |
9:16 |
9:16|16:9|1:1|4:5 |
--fps |
30 |
frame rate (lower = faster render) |
--seed |
7 |
RNG seed for typing jitter (change for a different "hand") |
--duration |
auto | force total seconds (≥ natural extends the ending hold) |
--theme / --title / --kicker / --model |
from JSON | overrides |
--avatar PATH |
– | composite avatar PiP overlay (locked pip clip) |
--layout / --corner |
pip-circle / br |
PiP placement |
--face-bias |
0.4 |
vertical crop of the PiP circle |
--out-dir / --slug |
broll_cursor/ / from filename |
output location + name |
--keep-frames |
off | keep the PNG sequence (debug) |
Integration with avatar-reel-composer
Each clip is a drop-in B-roll scene:
{ "id": "s1", "type": "broll", "broll_source": "existing",
"broll_clip": "broll_cursor/001_session.mp4", "text": "…" }
Subtitles belong to the reel, not the clip. Don't bake captions into the chat or
the avatar — avatar-reel-composer's finishing pass burns the segment subtitles over
the whole 9:16 frame. The only text this skill draws is the chat's own content +
the optional kicker label.
When the clip is trimmed to a short hook slot (the composer trims a B-roll to its
scene's narration length, from the start): front-load the session — faster
typing, low assistant.latency_ms, short tool duration_ms — so the prompt lands
and the first skill rows tick within the first ~3s.
Notes
- Base clips are silent; the avatar carries audio.
- Rendering is one screenshot per frame, so cost scales with
fps × duration. Drop--fpsfor quick drafts. - See REFERENCE.md for the full session schema, the timeline model, theming, and troubleshooting.