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

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:

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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →