# How the Archify visual-check Command Captures Screenshots Using Chrome DevTools Protocol

> Learn how the archify visual-check command captures screenshots using Chrome DevTools Protocol. It executes Page.captureScreenshot to save PNGs of rendered HTML artifacts.

- Repository: [tt-a1i/archify](https://github.com/tt-a1i/archify)
- Tags: how-to-guide
- Published: 2026-08-14

---

**The `archify visual-check` command launches a headless Chrome instance and uses the Chrome DevTools Protocol (CDP) to execute `Page.captureScreenshot`, saving PNG files of rendered HTML artifacts at specific viewports.**

The `visual-check` command in the `tt-a1i/archify` repository automates visual regression testing by driving a headless browser to capture pixel-perfect screenshots of delivered HTML files. Unlike traditional screenshot tools that rely on external APIs, this implementation embeds a custom Chrome controller that communicates via the Chrome DevTools Protocol pipe. This approach allows the `visual-check command` to capture screenshots without dependencies on third-party services, operating entirely within the local environment using the Chrome or Chromium executable already present on the system.

## Chrome Discovery and Launch Process

Before any screenshots can be captured, the command must locate and spawn a compatible Chrome binary.

### Locating the Chrome Executable

The process begins with `findChrome()` (approximately lines 101‑133 in `archify/bin/visual-check.mjs`), which searches the host system for a Chrome or Chromium executable. The function first checks the `ARCHIFY_CHROME` environment variable for a custom binary path, then falls back to standard system locations. If no executable is found, the command exits early with a *skipped* receipt status, ensuring the pipeline continues without failure when Chrome is unavailable.

### Spawning the Headless Browser Instance

Once located, the `ChromeVisualBrowser` class (lines 135‑165) spawns Chrome with a carefully selected set of security and stability flags. The process attaches to CDP through a pipe using the `--remote-debugging-pipe` argument, which eliminates the need for a network port and reduces attack surface. Chrome launches with a temporary user-data directory to ensure clean, isolated sessions free from cached state or cookies that could affect screenshot consistency.

## CDP Session Management and Page Setup

With Chrome running, the command establishes a communication channel and prepares the browser context for rendering.

### Attaching to the Target Page

The `attach()` method (lines 268‑282) creates or reuses a page target within the browser instance and enables the required CDP domains. Specifically, it activates the `Page` and `Runtime` domains, which provide the screenshot capture capabilities and JavaScript execution context needed for later metric collection. The method stores the CDP session ID, which serves as the routing key for all subsequent command exchanges.

### Configuring Viewport and Theme

Before navigation, the `inspect()` method (lines 284‑306) calls `Emulation.setDeviceMetricsOverride` to force the browser to the exact width and height specified for the current viewport test. It then navigates to the artifact URL, optionally appending a `theme` query parameter to test both light and dark mode renders. This ensures that the `visual-check command` captures screenshots at precise dimensions, matching the responsive breakpoints defined in the project configuration.

## The Screenshot Capture Mechanism

The core screenshot functionality resides within the `inspect()` method at lines **317‑324** of `archify/bin/visual-check.mjs`. When processing the two extreme viewport sizes designated for capture (as opposed to intermediate checks), the command issues the `Page.captureScreenshot` CDP command:

```javascript
const capture = await this.cdp.send(
  'Page.captureScreenshot',
  {
    format: 'png',
    fromSurface: true,
    captureBeyondViewport: false,
  },
  sessionId,
  20000
);
fs.writeFileSync(screenshotPath, Buffer.from(capture.data, 'base64'));

```

Chrome returns a base64-encoded PNG string, which the command decodes and writes atomically to the filesystem. The `screenshotPath` is computed by `sidecarPaths()` (lines 54‑69), ensuring that screenshot files are stored as sidecar artifacts adjacent to their source HTML files. The `fromSurface: true` parameter captures the composited surface pixels, while `captureBeyondViewport: false` restricts the screenshot to the visible viewport area, preventing full-page scroll captures that would deviate from the specified dimensions.

## Metrics Collection and Result Assembly

Immediately following the screenshot capture, `inspect()` queries layout metrics using `Runtime.evaluate` to determine inner dimensions, scroll dimensions, and the resolved color theme. The `observation()` function (lines 52‑70) later analyzes these metrics to detect viewport overflow conditions, flagging when content exceeds the expected container boundaries.

Once all viewports have been processed, `runVisualCheck()` aggregates results into a JSON receipt that lists each screenshot file path, an "ok" flag indicating success, and overflow status indicators. The command also generates a contact-sheet HTML page providing a visual overview of all captured screenshots for human review.

## Summary

- **Chrome Discovery**: The command uses `findChrome()` to locate the system Chrome binary or respects the `ARCHIFY_CHROME` environment variable fallback.
- **Headless Launch**: `ChromeVisualBrowser` spawns Chrome with `--remote-debugging-pipe` for secure, port-free CDP communication.
- **CDP Orchestration**: The `attach()` and `inspect()` methods manage CDP sessions, configure viewports via `Emulation.setDeviceMetricsOverride`, and navigate to artifact URLs.
- **PNG Generation**: Screenshots are captured using the `Page.captureScreenshot` CDP method with specific parameters (`format: 'png'`, `fromSurface: true`) and written to sidecar paths determined by `sidecarPaths()`.
- **Custom Protocol Handler**: All CDP communication flows through a custom `PipeCdp` implementation that handles request-response framing with null delimiters and 20-second timeouts.

## Frequently Asked Questions

### What happens if Chrome is not installed on the system?

If `findChrome()` cannot locate a Chrome or Chromium executable and the `ARCHIFY_CHROME` environment variable is not set, the command exits immediately with a *skipped* receipt status. This graceful degradation ensures that CI pipelines continue running even when the visual testing environment lacks a browser, though no screenshots will be generated.

### Why does the command use `--remote-debugging-pipe` instead of a network port?

The `ChromeVisualBrowser` class launches Chrome with `--remote-debugging-pipe` to establish a stdio-based CDP connection rather than the default WebSocket or HTTP interfaces. This approach improves security by avoiding open network ports and simplifies process management, as the pipe lifecycle ties directly to the parent Node.js process.

### What do the `fromSurface` and `captureBeyondViewport` parameters control?

According to the source code at lines 317‑324, `fromSurface: true` directs Chrome to capture the compositor surface pixels, ensuring accurate rendering of CSS transforms and animations. Setting `captureBeyondViewport: false` restricts the screenshot to the current viewport dimensions defined by `Emulation.setDeviceMetricsOverride`, preventing the capture of scrolled content outside the specified width and height boundaries.

### How are screenshot file paths determined?

The `sidecarPaths()` function (lines 54‑69) computes screenshot destinations by transforming the source artifact's file path and appending viewport-specific identifiers and the `.png` extension. This sidecar pattern keeps visual assets organized alongside their corresponding HTML files, using atomic writes to prevent partial file corruption during the capture process.