# How to Capture Screenshots and DOM Dumps During Browser Trace Sessions

> Learn to capture screenshots and DOM dumps during browser trace sessions with browserbase skills. Enhance your automation debugging without blocking your main processes.

- Repository: [browserbase/skills](https://github.com/browserbase/skills)
- Tags: how-to-guide
- Published: 2026-05-01

---

**The browser-trace skill runs three independent Node.js processes—Firehose, Sampler, and Bisector—to capture CDP protocol events, periodic screenshots, and DOM snapshots without blocking your primary automation.**

The `browserbase/skills` repository provides a zero-interference observability pipeline for browser automation that lets you capture screenshots and DOM dumps during trace sessions in real time. By leveraging the Chrome DevTools Protocol (CDP) and detached child processes, the tracer records visual state and page structure independently of your main testing framework.

## The Three-Component Architecture

The browser-trace skill splits observability into three isolated components that run concurrently. This design ensures that heavy I/O operations never stall your automation.

### Firehose: Streaming CDP Events

The **Firehose** component opens a persistent WebSocket connection to the browser and streams every CDP event to `cdp/raw.ndjson` as newline-delimited JSON. In `skills/browser-trace/scripts/start-capture.mjs` (lines 45‑54), the script spawns `browse cdp …` as a detached child process, allowing the firehose to survive even if the parent shell exits.

### Sampler: Periodic Screenshots and DOM Dumps

The **Sampler** runs a tight loop that captures visual and structural state every *N* seconds (default 2 s). Located in `skills/browser-trace/scripts/snapshot-loop.mjs` (lines 1‑20), this script:
- Calls `browse screenshot` to generate PNG files
- Dumps the DOM to HTML via temporary `.partial` files that are renamed only on success (lines 39‑46)
- Fetches the current URL using `browse --ws … --json get url` (lines 52‑58)
- Appends metadata to `index.jsonl` (lines 61‑63)

Screenshots are validated before writing; empty files are discarded to prevent zero-byte artifacts (lines 35‑37).

### Bisector: Post-Processing and Aggregation

After the session ends, `skills/browser-trace/scripts/bisect-cdp.mjs` parses the raw firehose and splits events into per-page buckets (`network/*.jsonl`, `console/*.jsonl`, `page/*.jsonl`, etc.). It also writes a high-level [`cdp/summary.json`](https://github.com/browserbase/skills/blob/main/cdp/summary.json) that aggregates per-page metrics, as documented in [`skills/browser-trace/SKILL.md`](https://github.com/browserbase/skills/blob/main/skills/browser-trace/SKILL.md) (lines 44‑48).

## Data Layout and File Structure

When you start a capture, the tracer creates a directory under `.o11y/<run-id>/`. The manifest is written by `start-capture.mjs` (lines 38‑44) immediately after directory creation.

A typical run produces this structure:

```

.o11y/<run-id>/
  manifest.json                # Run metadata: target port, interval, domains

  index.jsonl                 # One line per sample: {ts, screenshot, dom, url}

  cdp/
    raw.ndjson                # Full CDP firehose

    summary.json              # High-level session summary

    network/*.jsonl           # Request/response buckets

    console/*.jsonl
    page/*.jsonl
    dom/all.jsonl
  screenshots/<iso-ts>.png   # One PNG per interval

  dom/<iso-ts>.html           # One HTML dump per interval

```

## Starting and Stopping Capture Sessions

### Starting the Capture

Run `start-capture.mjs` with the target debugging port and optional run ID:

```bash
node scripts/start-capture.mjs 9222 my-run

```

This script:
1. Creates `.o11y/my-run/`
2. Writes [`manifest.json`](https://github.com/browserbase/skills/blob/main/manifest.json)
3. Spawns the Firehose and Sampler as detached processes
4. Returns the PIDs for later termination

### Running Your Automation

While the tracer runs, drive the browser using any CDP client (Playwright, Puppeteer, or `browse` CLI). Because the tracer uses its own read-only CDP connections, it never sends commands to the page and therefore cannot interfere with state or timing.

### Stopping and Bisecting

Terminate the session and process the results:

```bash
node scripts/stop-capture.mjs my-run
node scripts/bisect-cdp.mjs my-run

```

The `stop-capture.mjs` script kills the Firehose and Sampler PIDs recorded in the manifest, then triggers the Bisector to generate [`cdp/summary.json`](https://github.com/browserbase/skills/blob/main/cdp/summary.json).

## Practical Code Examples

### Quick Start with Local Chrome

Launch Chrome with remote debugging, then attach the tracer:

```bash

# 1. Start Chrome with CDP enabled

"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
  --remote-debugging-port=9222 \
  --user-data-dir=/tmp/chrome-o11y \
  about:blank &

# 2. Start the tracer (creates .o11y/<run-id>/)

node scripts/start-capture.mjs 9222 my-run

# 3. Run your automation

browse env local 9222
browse open https://example.com

# 4. Stop and process

node scripts/stop-capture.mjs my-run
node scripts/bisect-cdp.mjs my-run

```

### Manual Sampler Mode

To capture screenshots and DOM dumps without the full CDP firehose:

```bash
node scripts/snapshot-loop.mjs ws://localhost:9222 \
  .o11y/manual-run 3   # 3-second interval

```

Press **Ctrl-C** when finished; the loop cleans up its PID file automatically and leaves screenshots and DOM dumps in the specified directory.

### Querying Captured Data

Inspect specific domains or errors using the query utility:

```bash

# Show failed network requests for page 0

node scripts/query.mjs my-run page 0 network/failed

# List all errors across pages

node scripts/query.mjs my-run errors

```

The query utility reads [`cdp/summary.json`](https://github.com/browserbase/skills/blob/main/cdp/summary.json) and the per-page JSONL buckets to filter results.

## Key Implementation Details

- **Zero Interference**: The Sampler uses independent `browse --ws` one-shot calls, so it never mutates page state or blocks the main automation thread.
- **Atomic Writes**: DOM dumps are written to `*.partial` files and renamed only after successful completion, preventing corrupted artifacts if the process is killed mid-write.
- **Screenshot Validation**: The Sampler checks file size before appending to `index.jsonl`; failed screenshots are omitted rather than recorded as empty entries.
- **Multiple Concurrent Clients**: CDP permits multiple WebSocket connections; the tracer attaches as a read-only observer, making it safe to start and stop at any point during a test.

## Summary

- **Three-process architecture**: Firehose streams CDP events, Sampler captures screenshots/DOM every 2 seconds, and Bisector post-processes into queryable buckets.
- **File locations**: Raw data lives in `.o11y/<run-id>/` with subdirectories for `screenshots/`, `dom/`, and `cdp/`.
- **Entry points**: Use `start-capture.mjs` to begin, `stop-capture.mjs` to end, and `bisect-cdp.mjs` to generate summaries.
- **Zero interference**: The tracer runs in detached processes with read-only CDP connections, ensuring no impact on primary automation performance or state.

## Frequently Asked Questions

### How does the tracer avoid interfering with my primary automation?

The browser-trace skill spawns independent child processes that open their own CDP WebSocket connections to the browser. Because these connections are read-only (listening for events or capturing screenshots) and never send commands that mutate page state, they run in parallel with your test framework without blocking or altering the execution flow.

### What happens if a screenshot or DOM dump fails during sampling?

Failed captures are handled gracefully. In `snapshot-loop.mjs`, screenshots are checked for non-zero file size immediately after capture (lines 35‑37), and empty files are discarded before logging. DOM dumps use atomic file operations: they write to a temporary `.partial` file and rename to the final `.html` extension only on successful completion (lines 39‑46), preventing zero-byte artifacts from appearing in the output directory.

### Can I adjust the sampling interval for screenshots and DOM dumps?

Yes. Pass the interval in seconds as the third argument to `start-capture.mjs` or `snapshot-loop.mjs`. For example, `node scripts/start-capture.mjs 9222 my-run 5` sets a 5-second interval between samples. The default is 2 seconds when omitted.

### Is it possible to run only the sampler without capturing the full CDP firehose?

Absolutely. Execute `node scripts/snapshot-loop.mjs <ws-url> <output-dir> <interval>` directly to capture only screenshots and DOM dumps. This mode skips the Firehose entirely, reducing overhead and disk usage if you only need visual snapshots and HTML dumps without network logs or console events.