Dashcam
Core Concepts

Screenshot Capture

Automatic screenshots at every milestone, powered by Playwright.

Screenshot Capture

Your agent just rewired the navigation component. The git diff shows 14 changed files. But what does the app look like now? Did the sidebar collapse? Did the mobile layout break? Is the new dropdown rendering in the right position?

Dashcam captures screenshots automatically at key moments throughout every session. When you scrub through the timeline, these screenshots appear in the Result panel — giving you a visual history of your application at every stage of the agent's work.

Trigger Points

Screenshots are not captured randomly. Dashcam fires the capture pipeline at moments when the UI is most likely to have changed:

After file writes — Every time Claude Code's Write or Edit tool completes (detected via the PostToolUse hook), Dashcam evaluates whether a screenshot is warranted. If a dev server is running and the minimum interval has elapsed, a capture fires.

On task completion — When the Stop hook fires, Dashcam takes a final screenshot to record the end state of the session.

On error — When a tool execution fails or an error event is detected, a screenshot captures what the application looked like at the moment of failure. These are invaluable for debugging — you can see the broken state the agent was trying to fix.

Periodic interval — During active work, Dashcam captures at a configurable interval (default: every 5 minutes) regardless of hook activity. This fills in gaps during long-running operations where individual tool completions may not change the UI.

~/.dashcam/config.json
{
  "capture": {
    "enabled": true,
    "minInterval": 30,
    "triggerTools": ["Write", "Edit"],
    "devServerPorts": [3000, 3001, 4000, 5173, 5174, 8080, 8081],
    "customUrl": null,
    "dedup": true
  }
}

Capture Methods

Playwright (Dev Server)

When a development server is detected — Dashcam scans common ports (3000-3999, 4000-4999, 5173, 8080) for an HTTP 200 response — Playwright launches a headless Chromium instance and navigates to the detected URL:

PostToolUse (Write) ──▶ Port scan ──▶ HTTP 200 on :3000 ──▶ Playwright screenshot

This captures the application exactly as a user would see it: rendered CSS, loaded images, dynamic JavaScript state. It is the gold standard for visual history.

If you are running your dev server on a non-standard port, set customUrl in your configuration:

~/.dashcam/config.json
{
  "capture": {
    "customUrl": "http://localhost:4200"
  }
}

Terminal Capture (CLI Applications)

When no dev server is detected — because you are building a CLI tool, a backend service, or a library — Dashcam falls back to terminal capture. It reads the tmux pane content and converts the ANSI output to an image. This preserves terminal colors, formatting, and test output.

Perceptual Hash Deduplication

Not every file write changes the UI. If your agent edits a utility function, a type definition, or a server-side route, the rendered application may look identical before and after. Capturing redundant screenshots wastes disk space and clutters the timeline.

Dashcam solves this with perceptual hashing. After every capture, a hash is computed from the image content (average hash, 8x8 grid). If the hash matches the previous capture, the new screenshot is discarded:

Capture ──▶ Compute perceptual hash ──▶ Compare to last hash
                                             │
                                     ┌───────┴────────┐
                                     │                 │
                                   Match            Different
                                     │                 │
                                   Skip            Save PNG
                                   (dedup)         + thumbnail

This means your timeline only shows screenshots that represent actual visual changes. A session that writes 200 files might produce only 15 unique screenshots — each one meaningful.

Perceptual hashing is enabled by default. Disable it with "dedup": false in your capture configuration if you want every trigger to produce a screenshot regardless of visual similarity.

Thumbnail Generation

Every saved screenshot also produces a 320x240 thumbnail. These thumbnails power the session browser — each session card displays the last captured thumbnail as its preview image, creating the YouTube-style browsing experience.

Thumbnails are stored alongside full-resolution captures:

~/.dashcam/captures/
  {session_id}/
    1711234567890-full.png       # Full resolution
    1711234567890-thumb.png      # 320x240 thumbnail
    1711234572100-full.png
    1711234572100-thumb.png

Storage

All captures are stored locally in ~/.dashcam/captures/, organized by session ID. Each capture is recorded in the SQLite captures table with metadata including file path, dimensions, file size, perceptual hash, and the URL that was captured.

Default retention is 30 days. Run dashcam prune --older-than 30d to clean up old captures, or configure automatic retention:

~/.dashcam/config.json
{
  "retention": {
    "screenshots": 30,
    "videos": 14
  }
}

Expect roughly 500MB per month of active use. Screenshots dominate storage. Enable deduplication (on by default) to keep this number down.

Video Recording for Test Runs

When Dashcam detects test execution — Playwright tests, Vitest, Jest, Cypress, or pytest — it can record the entire test run as video. This is triggered automatically when PostToolUse events contain recognized test commands.

Videos are recorded using Playwright's built-in video recording in WebM format:

Test command detected ──▶ Start Playwright recording ──▶ Test completes ──▶ Save WebM

Recorded videos appear on the timeline as blue square markers. Click one in the Result panel to play it back with standard video controls. This is especially valuable for visual regression testing — you can watch exactly what the browser did during a test run that the agent triggered.

~/.dashcam/config.json
{
  "video": {
    "enabled": true,
    "triggerCommands": ["playwright test", "vitest", "jest", "pytest", "cypress run"],
    "quality": "medium",
    "maxDuration": 600
  }
}

On this page