Dynamo is the headless AI Visual & Video Harness for coding agents, browser automation and computer-use sandboxes.
It runs the virtual display, records every session, and compiles it into
VLM-ready keyframes and PR-ready MP4/GIF artifacts — in one context manager.
Agents act blind. Reviewers trust logs. Nobody sees what happened.
01
Raw video blows the context window
Sample a real session on the wall clock, the way the hand-rolled workflow does, and you pay for a thousand-plus frames. In our run, 19 of every 50 consecutive frames were pixel-identical — the model paid full price for an unchanged screen.
02
DIY ffmpeg scripts break in CI
Race conditions on Xvfb startup, and corrupted MP4s from SIGKILL. Our DIY workflow lost its file to a missing moov atom on every run; the harness kept the file and all four logged actions. The 40 MB GIF claim we started with was fiction, by the way — both GIFs measured about 2 MB.
03
Screenshots lack causality
A single PNG can’t tell you which click caused the regression. Without timestamped telemetry, visual evidence is just a guess.
Architecture
Six modules. One harness. From framebuffer to pull request.
Each layer is independently usable, fully typed, and battle-tested against headless Linux edge cases.
display.py
Virtual Display Manager
Owns the full Xvfb lifecycle. Configurable display number, resolution and DPI emulation, plus a readiness probe that blocks until X accepts connections.
display:99
resolution1920x1080x24
readinesssocket probe
recorder.py
Stream Recorder Daemon
Non-blocking ffmpeg -f x11grab in the background. Graceful SIGINT shutdown finalizes the moov atom — no corrupted containers, ever.
framerate15–30 fps
shutdownSIGINT · clean
telemetryJSONL events
processor.py
Post-Processing Engine
CRF-tuned H.264 / WebM for crisp code legibility. Two-pass palettegen → paletteuse GIFs small enough for GitHub. Optional click-ring overlays from telemetry.
codecH.264 · WebM
gif palette128/256 · bayer
overlayrings · crosshair
token_reducer.py
VLM Vision Pre-Processor
Keeps only frames that matter: scene-change detection plus exact frames at every logged action. Downsampled, deduplicated, labeled and timed for your LLM payload.
selectgt(scene,0.05)
anchorsaction timestamps
outputJPEG/WebP + JSON
harness.py
Python SDK & CLI
One ergonomic VisualHarness context manager wires everything together. Same capabilities from the CLI for shell-driven agents and CI jobs.
apiwith VisualHarness()
clidynamo record
typingfully typed
vision.py
Model Routing Layer
Routes each frame to an open-weight model, or to no model at all: skip unchanged frames, let a cheap text decision model score the survivors, and crop what is left to what the diff says moved. Records what the model was actually charged beside what we estimated.
primaryqwen3-vl:4b (Apache-2.0)
filterlaya typed decisions
depsnone — urllib
Developer experience
Wrap your agent in four lines.
No daemons to babysit, no display variables to export, no ffmpeg flags to memorize. Enter the context, act, exit — artifacts are finalized automatically.
Instant screenshotstake_screenshot() returns bytes mid-run for in-loop agent reasoning.
Action telemetrylog_action() timestamps every click, key and scroll for overlays and keyframe anchors.
One export callMP4, GIF and VLM frame directory with a typed Artifacts result.
from agent_visual_harness importVisualHarness# Usage in agent script:withVisualHarness(display=":99", resolution=(1920, 1080), record=True) as harness:
# 1. Agent runs GUI actions or Playwright tests here...
harness.log_action("click", x=500, y=300)
# 2. Grab instant viewport screenshot during execution
screenshot_bytes = harness.take_screenshot()
# Upon exit, files are finalized automatically:
artifacts = harness.export(
video_path="output/repro.mp4",
gif_path="output/demo.gif",
vlm_keyframes_dir="output/vlm_frames/"
)
print(f"Generated {len(artifacts.keyframes)} frames for VLM context.")
# Start a display + recorder, run any command inside it$ dynamo record --display :99 --resolution 1920x1080x24 --fps 24 \
-- python run_agent.py
# Compile artifacts from a finished session$ dynamo export ./session \
--mp4 output/repro.mp4 --crf 23 \
--gif output/demo.gif --max-colors 128 --dither bayer \
--overlay-clicks
# Produce a token-budgeted keyframe set for your VLM$ dynamo keyframes ./session --scene 0.05 --max-size 1280 \
--format webp --out output/vlm_frames/
✓ 29 keyframes · 1,042 frames scanned · manifest.json written
FROM python:3.12-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
xvfb ffmpeg x11-utils && rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir agent-visual-harness playwright \
&& playwright install --with-deps chromium
WORKDIR /app
COPY . .
# Dynamo manages Xvfb itself — no DISPLAY export neededENTRYPOINT ["dynamo", "record", "--", "python", "agent.py"]
Stop paying to show your model the same frame twice.
Estimate how much visual context Dynamo removes before it ever reaches your multimodal model.
Raw frames1,0401.14M tokens
Dynamo keyframes3033.0K tokens
Raw
Dynamo
97.1%
fewer visual tokens per session — with every action-anchored frame preserved.
The measurements
We ran the benchmark. It corrected us.
A real Chromium drives a real checkout — card declined twice, retried, captured on the third attempt — and the same 29 keyframes reach a local Qwen3-VL-4B three different ways. Two token columns, because they disagree.
How the frames reach the model
Frames sent
Estimated tokens
Counted by the model
A · every keyframe, whole
29
22,707
32,132
B · cropped to what the diff says moved
29
10,947
32,040
C · skip unchanged → filter → crop
24
8,575
26,528
Cropping saved nothing
We estimated a 52% cut and were wrong. The server resizes every image to a fixed budget before the vision encoder sees it: 1280×800 and 128×80 both cost 1,108 tokens, and a 25×16 crop cost more. The real bill moved 0.3%.
Skipping paid 82.6%
A cheap text decision model (Laya) scored each frame and dropped 5 of 29 — p = 0.05 to 0.10. Not sending the frame is the lever that actually reduces what you are charged.
Three bugs only a clean machine sees
An 8-byte stub frame that walked past a zero-byte guard, a single-threaded storefront that stalled the browser, and a gallery gated on a file written four lines later. All fixed, all tested.
The GIF is not smaller
Our own two-pass palette came out 4% larger than the naive GIF on dense browser chrome. We changed the caption rather than keep the claim.
Outputs
Every run ships evidence, not just exit codes.
repro.mp4
Legible repro video
CRF-tuned H.264 keeps monospace text crisp at a fraction of raw size. Attach to bug reports and incident reviews.
demo.gif
PR-ready looping GIF
Two-pass palette generation with bayer dithering. Small enough to inline in a GitHub PR description.
vlm_frames/
Labeled keyframes
Deduplicated JPEG/WebP frames with a timing manifest — drop straight into a multimodal LLM context payload.
telemetry.jsonl
Action timeline
Every agent action with a precise timestamp. Correlate visual state to cause for debugging and evals.
Runs entirely in your VPC, air-gapped clusters, or ephemeral sandboxes.
Fleet-scale headless
Isolated display numbers per worker. Run hundreds of parallel agent sessions per node pool.
Audit-grade evidence
Immutable video + action timelines for agent governance, compliance and incident review.
CPU-only, low overhead
No GPU dependency. Tunable framerate and CRF to fit tight sandbox budgets.
Model-agnostic
Keyframes work with any multimodal model or in-house VLM — no vendor lock-in.
Zero runtime dependencies
Standard library only. ffmpeg and tesseract are optional extras, not requirements.
FAQ
Questions engineers ask first.
What does Dynamo need on the host?
A Linux environment with Xvfb and ffmpeg installed, and Python 3.10+. It's designed for Docker, Kubernetes pods, CI runners, and computer-use sandboxes — no physical display or GPU required.
Does it work with Playwright, Selenium or computer-use agents?
Yes. Anything that renders to an X11 display can be recorded. Launch your browser or GUI inside the VisualHarness context and Dynamo captures it; call log_action() from your agent loop to anchor telemetry.
How does the token reducer decide which frames to keep?
It combines FFmpeg scene detection (e.g. select='gt(scene,0.05)') or image-difference scoring with exact frames at each logged action timestamp, then deduplicates and downsamples to your max bounding box.
What happens if my agent crashes mid-recording?
The context manager sends SIGINT to the recorder on exit — including on exceptions — so FFmpeg finalizes the moov atom and the MP4 stays playable.
Is any data sent to Dynamo's servers?
No. Dynamo is a local Python package. All recordings, frames and telemetry are written to paths you specify.
Get started
Ship agents you can actually see.
Tell us about your agent stack. An engineer — not a sales rep — will reply within one business day. Inquiries and demo requests are delivered directly to smasherthedevansh@gmail.com.
Integration walkthrough for your sandbox
Token-budget tuning for your VLM
Fleet deployment & support options
Demo requests routed to smasherthedevansh@gmail.com