A tiny, reusable rig for turning any terminal program or GUI command into an
MP4 — headlessly, in a container, with no display attached. Agents (or CI)
can use it to record demos, TUIs, dashboards, or app walkthroughs into a video
without a real screen. It also renders self-contained HTML into still PNG cards
(social cards, diagrams, charts) via render-card.sh (see below).
It's just the recording machinery: Xvfb (virtual X) → i3 (fullscreen the
window) → xterm (for terminal programs) → ffmpeg x11grab (capture) →
out.mp4, with optional x11vnc for a live preview. What you record is up to
you — mount your code, pass a command.
A clip this rig produced, the duel example (two engines
racing side by side at their real measured speed, with a branding outro):
# record a terminal program / TUI
./record.sh "python3 demo.py" demo.mp4
# knobs are env vars
WIDTH=1080 HEIGHT=1080 DURATION=20 FONTSIZE=18 ./record.sh "python3 app.py" square.mp4
# record a GUI app instead of a terminal program
GUI=1 ./record.sh "mpv --fullscreen clip.mp4" play.mp4
# watch it render live while recording (VNC on :5900)
VNC=1 ./record.sh "python3 demo.py" demo.mp4record.sh builds the image once, mounts your current dir at /work and
./out at /out, runs the command, and writes the video to ./out/.
Or call the image directly:
docker build -t recorder-for-agents .
docker run --rm -v "$PWD:/work" -v "$PWD/out:/out" \
-e CMD="python3 demo.py" -e OUT=demo.mp4 -e DURATION=12 -e FONTSIZE=18 \
recorder-for-agents| var | default | meaning |
|---|---|---|
CMD |
(required) | command to run and record |
OUT |
out.mp4 |
output filename under /out |
WIDTH/HEIGHT |
1280/720 |
frame size (use 1080/1080 for square, 1080/1920 for vertical) |
DURATION |
15 |
seconds to record |
FPS |
30 |
capture frame rate |
FONTSIZE |
16 |
xterm font size (terminal mode) |
GUI |
0 |
1 = run CMD as a windowed app instead of inside xterm |
VNC |
0 |
1 = expose x11vnc on VNC_PORT (5900) for live preview |
START_DELAY/END_HOLD |
1.0/1.5 |
pad before first frame / after CMD exits |
FONT/BG/FG |
DejaVu/black/white | xterm look |
Python + rich are preinstalled (agent TUIs are often rich-based); for other
runtimes, pip install/apt-get inside CMD or extend the Dockerfile.
- The command should finish within
DURATION(it's a fixed-length capture). - Output is silent; mux audio afterward with ffmpeg if needed.
- A real consumer of this rig: the parakeet.cpp "transcription race" demo —
it just provides a
richrenderer + data and calls this recorder.
render-card.sh is the photo counterpart to record.sh: it turns a
self-contained HTML file into a crisp PNG (social card, diagram, chart, poster).
Headless Chrome screenshots the page at a high device-scale-factor, then
ImageMagick crops to the exact target size. No display, no Docker, no image
model: just the browser you already have.
# 1600x900 @2x by default -> a 3200x1800 retina PNG next to the .html
./render-card.sh examples/cards/card.html
# size + name via env knobs
WIDTH=1200 HEIGHT=675 ./render-card.sh examples/cards/card.html twitter.png
SCALE=3 ./render-card.sh examples/cards/chart.html chart@3x.pngKnobs (env): WIDTH/HEIGHT target size (default 1600/900), SCALE retina
multiplier (default 2), PAD extra render height that prevents Chrome's short
headless viewport from clipping the footer (default 220), DELAY ms to let
fonts/layout settle, CHROME to pick a specific binary.
Your HTML sizes itself to the target (html,body{width:1600px;height:900px})
and may pull Google Fonts (Chrome has network during render).
The recipe, not the tool, is what matters:
- Steal the palette from a real brand asset. Pull the dominant colors from a
logo and build from those, instead of inventing a scheme:
magick logo.png -resize 50x50 -colors 8 -format "%c" histogram:info: - Commit to one opinionated direction and avoid the 2024-era AI tells: no cyan-on-dark, no purple gradients, no soft drop shadows, no Inter/Roboto. The examples go warm-paper retro-industrial: tinted neutrals, hard offset block shadows, a faint engineering grid, distinctive display + text fonts.
- Draw diagrams as code, not as art. The card's nodes and arrows are SVG generated by a small JS loop, so geometry is exact and crisp at any scale and trivially editable (change a coordinate, re-run).
examples/hello/— a trivial progress-bar TUI to smoke-test the rig.examples/duel/— a full "processing race" comparison video: two engines side by side, progress bars, stats card, and a configurable branding outro (plus an optional subtle CRT pass). Bring your own traces.examples/image_race/— the image counterpart: renders frames directly with Pillow (no recorder/Docker) so the content can be real images — two engines labeling the same photo, detection boxes popping in over each one's real time, with a search pill showing the query. Use it when a terminal can't show what you need to show. mp4 + gif, in 16:9 / 1:1 / 9:16.examples/cards/— two still cards forrender-card.sh: a conceptcard.html(before/after routing diagram) and achart.html(bar chart), both in a warm retro-industrial palette. Edit the text/colors and re-render.
