---
title: "Video Recording"
description: "Browser automation CLI for AI agents"
canonical_url: "https://agent-browser.dev/recording"
---

# Video Recording

Use `record` to capture browser automation as a WebM or MP4 video for debugging, CI evidence, product walkthroughs, or repro reports.

## Requirements

Recording pipes frames into `ffmpeg`, which must be on `PATH` with the `libvpx` and `libx264` encoders (the default builds from Homebrew and Debian/Ubuntu include both):

```bash
brew install ffmpeg        # macOS
sudo apt install ffmpeg    # Debian/Ubuntu
agent-browser doctor       # reports ffmpeg and its encoders under "Recording"
```

The supported formats are `.webm` (VP8 via libvpx) and `.mp4` (H.264 via libx264). Any other extension is handed to ffmpeg as-is with H.264 video, so `.mkv` or `.mov` work if ffmpeg knows the container. A path with no extension is rejected before recording starts, since ffmpeg would have nothing to pick a container from. Nothing else in agent-browser needs ffmpeg.

## Basic workflow

```bash
agent-browser open https://example.com
agent-browser record start ./demo.webm
agent-browser snapshot -i
agent-browser click @e1

agent-browser record stop
```

After launching a session, `record start` can also navigate immediately:

```bash
agent-browser open
agent-browser record start ./demo.webm https://example.com
```

`record start` records the current active page as-is. With no URL, nothing is navigated or replaced: the recorder attaches to the tab you already have open, so capture begins on a hydrated page rather than at `load` on a cold navigation. With a URL, the active tab navigates there first and recording starts once the page has loaded. No extra tab or browser context is created.

To record in a separate tab, open one first:

```bash
agent-browser tab new https://example.com
agent-browser record start ./demo.webm
```

## Visible cursor

Chrome recordings do not normally include the native pointer. Add `--cursor` for an animated pointer and click ripple rendered with the page, keeping drags synchronized in every captured frame. The temporary overlay is inert, hidden from accessibility snapshots, and removed when recording stops. Screenshots taken during the recording include it:

```bash
agent-browser record start ./walkthrough.webm --cursor
```

## Contact sheets

Add `--contact-sheet` for a timestamped PNG with highlighted changes.

```bash
agent-browser record start ./checkout.webm --contact-sheet

# Select a frame when at least 2% of pixels change
agent-browser record start ./checkout.webm --contact-sheet-threshold 0.02
```

The threshold accepts `0` to `1`, defaults to `0.05`, and implies `--contact-sheet`. Contact sheets contain at most 100 frames.

![Example contact sheet with timestamps and highlighted change regions](/recording/contact-sheet-example.png)

## Frame rate

Recording captures 30 frames per second by default, which is enough for scrolling, hover states, and CSS transitions to read as motion rather than as a slideshow. Use `--fps` to change it:

```bash
# 60 fps for a short, motion-heavy take
agent-browser record start ./scroll.webm --fps 60

# 10 fps for a long soak run where file size matters more than motion
agent-browser record start ./soak.webm --fps 10
```

<table>
  <thead>
    <tr><th>Rate</th><th>When to use it</th></tr>
  </thead>
  <tbody>
    <tr><td>60</td><td>Drag interactions, animation and scroll polish work, anything where a single frame is the evidence. Best on short clips.</td></tr>
    <tr><td>30 (default)</td><td>Everything else: flows, CI evidence, walkthroughs.</td></tr>
    <tr><td>1 to 15</td><td>Long sessions where the video is a timeline rather than a motion study.</td></tr>
  </tbody>
</table>

Valid rates are 1 to 60. The latest Chrome frame is held between repaints so the file duration matches wall clock.

`record stop` reports `frames` written to the file and distinct `capturedFrames` received from Chrome.

## Commands

<table>
  <thead>
    <tr><th>Command</th><th>Description</th></tr>
  </thead>
  <tbody>
    <tr><td><code>record start &lt;path.webm|path.mp4&gt; [url] [--fps &lt;n&gt;] [--cursor] [--contact-sheet]</code></td><td>Start recording the active page; a URL navigates the active tab first</td></tr>
    <tr><td><code>record stop</code></td><td>Stop the active recording and save the file</td></tr>
    <tr><td><code>record restart &lt;path.webm|path.mp4&gt; [url] [--fps &lt;n&gt;] [--cursor] [--contact-sheet]</code></td><td>Stop the current recording and immediately start another</td></tr>
  </tbody>
</table>

## CI evidence

```bash
#!/bin/bash
set -e

cleanup() {
  agent-browser record stop 2>/dev/null || true
  agent-browser close 2>/dev/null || true
}
trap cleanup EXIT

agent-browser open https://app.example.com/login
agent-browser record start "./artifacts/login-flow.webm"
agent-browser snapshot -i
agent-browser fill @e1 "demo@example.com"
agent-browser fill @e2 "password"
agent-browser click @e3
agent-browser wait --url "**/dashboard"
```

Keep recordings as CI artifacts when browser failures are hard to diagnose from text output alone.

## Human-readable demos

Add small waits when the video is meant for a person to watch:

```bash
agent-browser open https://shop.example.com
agent-browser record start ./checkout.webm
agent-browser wait 500
agent-browser click @e4
agent-browser wait 500
agent-browser screenshot ./screenshots/cart.png
agent-browser record stop
```

Screenshots and videos work well together: screenshots capture precise still states, while the video shows timing, transitions, and unexpected overlays.

## Output format

<table>
  <thead>
    <tr><th>Property</th><th>Value</th></tr>
  </thead>
  <tbody>
    <tr><td>Container</td><td>WebM (<code>.webm</code>, VP8 via libvpx) or MP4 (<code>.mp4</code>, H.264 via libx264), chosen by the extension; other extensions get H.264 in whatever container ffmpeg maps them to</td></tr>
    <tr><td>Encoder</td><td>ffmpeg on <code>PATH</code></td></tr>
    <tr><td>Frame rate</td><td>30 fps by default, 1 to 60 with <code>--fps</code></td></tr>
    <tr><td>Viewport</td><td>Uses the active browser viewport settings</td></tr>
    <tr><td>State</td><td>Records the active page in place, including its session and in-page state</td></tr>
  </tbody>
</table>

## Limitations

- Recording adds overhead to automation, and higher frame rates add more.
- Long recordings can use significant disk space; 60 fps roughly doubles the bitrate of 30 fps.
- Distinct frames per second are bounded by how often the page repaints, so a page that renders below 60 fps records below it too.
- Use `record stop` before closing a session if you need the file flushed.
- Some constrained headless environments may have codec or GPU limitations. An ffmpeg built without libvpx or libx264 cannot write the matching format; `agent-browser doctor` reports which encoders are present.
