ScreenshotNeo

BlogHow-to

Puppeteer screenshot differs from Chrome: match browser settings

Match browser version, headless mode, viewport, screen size and screenshot options before investigating why Puppeteer and Chrome images differ.

By the ScreenshotNeo team4 October 20266 min read

If a Puppeteer screenshot differs from Chrome, first make sure both captures use the same browser build and mode, page viewport and device scale, browser screen dimensions, screenshot options, and page state. Change one variable at a time. These settings create a controlled comparison; they cannot guarantee pixel identity across different operating systems, fonts, or page behavior.

1. Record the browser and rendering mode

Puppeteer launches headless Chrome by default. Current headless Chrome and the legacy chrome-headless-shell are separate choices, and the shell does not completely match regular Chrome behavior. Puppeteer v20 and later uses Chrome for Testing; headful and current headless modes share a browser code path. Still, do not assume that a user-installed Chrome and Puppeteer’s bundled browser are the same build.

Before changing page CSS, note:

  • Puppeteer package version and actual browser version.
  • Whether the reference was captured in Chrome headful, current headless, or legacy headless-shell mode.
  • Operating system and, if known, font environment.
  • The exact URL and the two image files being compared.

For a controlled baseline, use Puppeteer’s bundled browser and regular current headless mode. If the reference is headful Chrome, make a second comparison in headful mode. Puppeteer documents the [headless modes](https://pptr.dev/guides/headless-modes), [supported browsers and release mapping](https://pptr.dev/chromium-support/), and [launch options](https://pptr.dev/api/puppeteer.launchoptions). Its compatibility guarantee applies to the bundled browser.

2. Set the page viewport and device scale before navigation

The page’s CSS viewport is separate from the browser window or screen. Explicitly set the reference width, height, device scale factor, and mobile/touch emulation before navigating. A change to isMobile or hasTouch can cause the page to reload, so set them up front.

const puppeteer = require('puppeteer');

(async () => {
  const url = 'https://example.com';
  const browser = await puppeteer.launch({ headless: true });
  try {
    const page = await browser.newPage();
    await page.setViewport({
      width: 1440,
      height: 900,
      deviceScaleFactor: 1,
      isMobile: false,
      hasTouch: false,
    });
    await page.goto(url, { waitUntil: 'networkidle2' });
    await page.screenshot({ path: 'puppeteer.png' });
  } finally {
    await browser.close();
  }
})();

Replace the example dimensions and scale with the values from the Chrome reference. They are not universal defaults. Puppeteer’s [Page.setViewport()](https://pptr.dev/api/puppeteer.page.setviewport) API documents these emulation settings.

3. Match browser screen and window dimensions

A page can read screen properties or respond to window geometry, so matching the CSS viewport alone may not be enough. In headless mode, the screen defaults to 800×600 unless --window-size is set. Puppeteer’s --screen-info is a headless-only control.

const browser = await puppeteer.launch({
  headless: true,
  args: ['--window-size=1440,900'],
});

Use the reference’s actual screen dimensions where relevant. Keep setViewport() in place: a window-size flag is not a substitute for explicitly setting the page viewport. See Puppeteer’s [screen configuration guide](https://pptr.dev/guides/screen-configuration).

4. Match the screenshot capture options

Confirm that both images show the same region and background. One may be a viewport screenshot while the other is full-page or clipped. Make capture behavior explicit so defaults do not obscure the comparison:

await page.screenshot({
  path: 'puppeteer.png',
  fullPage: false,
  omitBackground: false,
  fromSurface: true,
});

These options mean a viewport capture, normal page background, and the default surface capture. Change them only to match the reference. Puppeteer’s [ScreenshotOptions](https://pptr.dev/api/puppeteer.screenshotoptions) documents clipping and other screenshot settings, and the [screenshots guide](https://pptr.dev/guides/screenshots) covers page and element captures.

5. Capture the same page state

Even with browser and geometry aligned, compare captures made after the same content has settled. The example uses networkidle2 as a starting point, but no single wait condition is right for every site. Check whether fonts, images, animations, lazy-loaded content, or asynchronous updates were still in progress in either capture. Use a page-specific readiness condition where necessary and make the capture timing reproducible.

If a mismatch remains, investigate the specific reproduction: operating system, available fonts, GPU or compositing behavior, color profile, and site-specific code may matter. These are variables to check, not a diagnosis by themselves. A reliable root-cause claim needs the exact page, both browser versions, operating system, settings, and image pair.

6. Compare settings in a fixed order

  1. Browser: product, build, Puppeteer version, and bundled versus user-installed Chrome.
  2. Mode: headful, current headless, or legacy headless-shell.
  3. Page emulation: viewport width and height, device scale factor, mobile and touch flags.
  4. Screen and window: screen dimensions and any window-size configuration.
  5. Capture: viewport versus full-page, clip, transparency, and capture surface.
  6. Page and host: readiness state, OS, fonts, and other environmental details.

Change one item per comparison and keep the rest fixed. This helps distinguish a geometry mismatch from a browser-mode or page-state difference without prematurely blaming CSS.

Common problems and fixes

Symptom Likely mismatch What to check
Text wraps differently or columns shift Viewport dimensions or device scale differ Set viewport width, height, and deviceScaleFactor before navigation.
Mobile layout differs from desktop Chrome Mobile or touch emulation is not aligned Match isMobile and hasTouch as well as viewport size.
Page logic reports different screen values Browser screen/window dimensions differ Check headless screen defaults and set window size when needed.
Whole-page height or background differs Full-page, clip, or transparency options differ Set capture region, fullPage, and omitBackground explicitly.
Only some text or images differ Capture occurred at a different page state, or environments differ Wait for the same content to settle; then inspect fonts, OS, and the specific reproduction.
Large rendering differences despite matching dimensions Browser builds or headless modes differ Record actual browser versions and compare current headless with the reference mode; do not treat legacy shell as interchangeable.

Performance, reliability, and cost

For repeatable comparisons, pin Puppeteer and its browser version, keep launch and capture settings in one script, and record them alongside the screenshot. A fixed readiness condition makes reruns easier to interpret, but sites with ongoing network activity may need a page-specific condition. Avoid drawing conclusions from a single capture when the page contains time-dependent or asynchronous content.

Local Puppeteer requires maintaining a browser runtime and the environment used for capture. If you need a screenshot endpoint rather than managing that setup, ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns a PNG, JPEG, WebP, or PDF, and its documented screenshot parameters include viewport, device, wait, and capture controls. See the [ScreenshotNeo documentation](https://screenshotneo.com/docs/).

Or skip the browser setup

Make a screenshot request with the target URL and your API key:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks, blank pages, timeouts, and failed loads are not billed, and cache hits cost nothing; responses include X-Page-Verdict and X-Billed headers. Its MCP server gives AI agents tools for screenshots, page information, and PDF capture. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. [Create a free ScreenshotNeo account](https://screenshotneo.com/account/sign-up/).

FAQ

Does setting the same viewport guarantee identical pixels?

No. It controls page geometry and emulation, but browser build, capture mode, content timing, operating system, and fonts can still differ.

Should I use headless-shell to match Chrome?

Only if the reference itself uses that legacy shell. For a first comparison, use Puppeteer’s current headless mode and bundled browser, then compare the mode that produced the reference.

What details should I include when asking for help?

Share the URL if it can be shared, both screenshots, Puppeteer and browser versions, OS, headful/headless mode, viewport and scale, screen dimensions, capture options, and how the page was awaited.