·SavePage Team

Using Headless Chrome for Website Screenshots

chromeheadlessguide

Headless Chrome is Chrome without the graphical interface. It can do everything a regular Chrome browser does -- render HTML, execute JavaScript, apply CSS, load images -- but it runs as a background process controlled programmatically.

For screenshots, this means you get pixel-perfect rendering that matches what users see in their browsers.

Why headless Chrome won

Before headless Chrome, the standard tools for programmatic screenshots were PhantomJS and SlimerJS. Both used older rendering engines with incomplete support for modern CSS and JavaScript. Websites often looked different in PhantomJS than in a real browser.

When Google released headless Chrome in 2017, it changed everything. Since it uses the same Blink rendering engine as the desktop browser, you get the same output. It supports all modern web standards: CSS Grid, Flexbox, Web Fonts, ES modules, WebAssembly, and everything else.

PhantomJS was archived in 2018. Headless Chrome is now the standard for browser automation and screenshot capture.

Chrome DevTools Protocol

Headless Chrome is controlled through the Chrome DevTools Protocol (CDP). This is the same protocol that Chrome DevTools uses to inspect pages. It provides commands for:

  • Navigating to URLs
  • Setting viewport dimensions
  • Emulating devices
  • Capturing screenshots
  • Intercepting network requests
  • Executing JavaScript in the page context

The protocol operates over WebSocket connections and uses JSON messages. Libraries like Puppeteer and Playwright provide higher-level abstractions over CDP, but you can also use it directly.

Screenshot capture commands

The CDP method for screenshots is Page.captureScreenshot. It accepts parameters for format (PNG or JPEG), quality (for JPEG), and clip region.

For a full viewport screenshot:

{
  "method": "Page.captureScreenshot",
  "params": {
    "format": "png"
  }
}

For a specific region:

{
  "method": "Page.captureScreenshot",
  "params": {
    "format": "png",
    "clip": {
      "x": 0,
      "y": 0,
      "width": 800,
      "height": 600,
      "scale": 1
    }
  }
}

The screenshot is returned as a base64-encoded string in the response.

Device emulation

CDP supports device emulation through Emulation.setDeviceMetricsOverride. This lets you set the viewport width, height, device scale factor, and mobile flag:

{
  "method": "Emulation.setDeviceMetricsOverride",
  "params": {
    "width": 375,
    "height": 812,
    "deviceScaleFactor": 3,
    "mobile": true
  }
}

This triggers the same responsive behavior that a real mobile device would. Media queries fire, touch-specific JavaScript runs, and the page layout adapts to the specified dimensions.

Performance considerations

Chrome is a heavy process. Each instance consumes 100-300 MB of memory depending on the page. Startup time is 1-3 seconds. Page rendering adds another 1-5 seconds depending on complexity.

For high-throughput screenshot systems, the key optimization is browser pooling: keeping a set of Chrome instances running and reusing them across requests instead of starting a new one each time.

When to use an API instead

If you need occasional screenshots (a few per day), running headless Chrome locally is fine. If you need hundreds or thousands per day, the operational overhead adds up: managing browser pools, handling crashes, monitoring memory, scaling infrastructure.

A screenshot API handles all of this for you. You trade infrastructure management for API calls.