ScreenshotNeo

BlogHow-to

How to Compare Website Screenshots at the Same Viewport Size

Compare website screenshots reliably by matching CSS viewport, browser, DPR, zoom, and page state. Includes a runnable Playwright workflow and practical diff guidance.

By the ScreenshotNeo team4 October 20267 min read

To compare website screenshots fairly, capture both versions with the same CSS viewport width and height, browser and version, operating system, zoom, device-pixel ratio (DPR), and page state. Wait for fonts, images, and dynamic content to settle. Then inspect the images side by side, use an aligned opacity overlay, or compare them against an approved baseline with a visual testing tool.

Matching screenshot file dimensions alone is not enough: the browser must render each page at the same CSS viewport. Responsive breakpoints depend on the browser viewport, and the same viewport can still produce pixel differences across environments.

1. Match the capture conditions

Before capturing, write down the conditions you need to keep constant. For a responsive site, repeat the comparison at every viewport that matters instead of resizing a screenshot after capture.

Setting What to keep consistent
CSS viewport Width and height in CSS pixels, set in the browser or capture tool.
Browser environment Browser project and version, operating system, and headed or headless mode.
Display scale Browser zoom and device-pixel ratio.
Page state Route, account or test data, scroll position, and interaction state.
Timing Wait for fonts, images, asynchronous content, and layout shifts to settle.
Volatile content Freeze data or hide narrowly scoped regions such as timestamps when they are irrelevant.

Playwright cautions that rendering can vary with the host OS, browser version and settings, hardware, power source, and headless mode. Run baseline creation and later comparisons in the same environment when possible. Its screenshot assertion waits for two consecutive screenshots to match before comparing with the expectation. Playwright visual comparisons · Playwright PageAssertions.

2. Capture both versions with Playwright

This runnable example opens the same route in two deployments, fixes the viewport, waits for fonts and images, and saves screenshots. Set the URLs to the versions you want to compare. Install Playwright and its Chromium browser first using the official Playwright installation guide.

// compare.mjs
import { chromium } from 'playwright';

const [beforeUrl, afterUrl] = process.argv.slice(2);
if (!beforeUrl || !afterUrl) {
  throw new Error('Usage: node compare.mjs <before-url> <after-url>');
}

const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
  viewport: { width: 1440, height: 900 },
  deviceScaleFactor: 1,
  colorScheme: 'light',
  locale: 'en-US',
  timezoneId: 'UTC'
});

async function capture(url, path) {
  const page = await context.newPage();
  await page.goto(url, { waitUntil: 'networkidle', timeout: 60000 });
  await page.evaluate(async () => {
    await document.fonts.ready;
    const images = [...document.images];
    await Promise.all(images.map(img => {
      if (img.complete) return Promise.resolve();
      return new Promise(resolve => {
        img.addEventListener('load', resolve, { once: true });
        img.addEventListener('error', resolve, { once: true });
      });
    }));
  });
  await page.screenshot({ path, fullPage: true, animations: 'disabled' });
  await page.close();
}

try {
  await capture(beforeUrl, 'before.png');
  await capture(afterUrl, 'after.png');
} finally {
  await context.close();
  await browser.close();
}

Run it with two URLs:

node compare.mjs https://staging.example.com/pricing https://production.example.com/pricing

networkidle is not a universal signal that a page is visually settled. Applications with polling or long-lived requests may never become idle; pages may also load content after network activity pauses. In those cases, wait for a meaningful selector or application-ready signal instead, and set a finite timeout. For example, replace the navigation wait with waitUntil: 'domcontentloaded', then await page.locator('[data-testid="page-ready"]').waitFor(). Use the same readiness rule for both captures.

The sample disables animations and waits for fonts and images. If a page has lazy-loaded images below the fold, scroll through the page before capturing or use your test’s explicit loading strategy. Keep the same scroll and interaction steps for both versions. For a viewport-only comparison, omit fullPage: true; for full-page screenshots, confirm both captures represent the same content height and state.

3. Compare side by side, overlay, or baseline

Side by side

Open both images at the same zoom and inspect matching regions. This is the fastest method for a one-time review, but subtle shifts can be hard to spot.

Opacity overlay

Place one image over the other at partial opacity. Misaligned edges appear doubled. The files must have matching dimensions and alignment; an overlay cannot correct captures taken at different CSS viewports or scroll positions.

Pixel diff and visual baselines

For recurring checks, save an approved baseline and compare future captures against it. Playwright Test uses pixelmatch and supports thresholds such as maxDiffPixels; it also supports a stylePath to hide or filter volatile elements. Thresholds can suppress antialiasing noise, but a permissive threshold can hide a real small change. Review the diff before accepting a new baseline. Playwright visual comparison options.

Chromatic documents Playwright integration, viewport widths, thresholds, and comparison to a baseline. Its documentation says Capture 9 uses DPR 2.0 and warns that different DPRs appear as changes; a fallback to DPR 1.0 can occur for certain browser image-size limits. Treat that as a product capture setting, not a general recommendation, and check its current documentation when configuring a project. Chromatic Playwright integration · Chromatic snapshots.

BrowserStack Percy can render stored DOM and page assets at responsive widths. Each configured width counts as a separate screenshot toward monthly usage, according to its documentation. Check current plan limits before choosing how many widths to run. Percy responsive visual testing.

4. Choose a workflow for the frequency of comparison

Approach Best fit Trade-offs
Manual side-by-side or overlay One-time review, migration check, or visual QA without test infrastructure. Fast to start; depends on consistent captures and human review.
Playwright Test Teams already using Playwright that want local or CI baseline checks. Requires a reproducible browser setup and baseline maintenance.
Chromatic Teams using its documented Playwright snapshot and review workflow. Hosted workflow; check current capture behavior, configuration, and plan details.
Percy Hosted responsive comparisons across selected widths. Each configured responsive width counts toward monthly screenshot usage.

For an isolated change, a consistent pair of screenshots and an overlay may be enough. For repeated releases, automate capture and baseline review so the conditions and approval history stay consistent.

5. Interpret diffs and update baselines carefully

A changed pixel is a signal to inspect, not proof of a defect. Differences can come from an actual layout change, late font loading, timestamps, rotating content, antialiasing, or a browser update. Conversely, a large tolerance or broadly masked region can conceal a meaningful regression.

  1. Open the diff and identify the affected component or region.
  2. Check whether the change is expected and whether capture conditions match.
  3. Investigate unexpected changes in layout, typography, assets, or state.
  4. Update the baseline only after reviewing and accepting the intended appearance.

6. Common problems and fixes

Symptom Likely cause Fix
Images have the same pixel size but layout differs. CSS viewport dimensions or zoom differ. Set the same CSS width and height and browser zoom before recapturing.
Many small text or edge diffs appear. Different browser, OS, DPR, font availability, or rendering mode. Use the same browser environment and wait for document.fonts.ready.
Screenshot is blank or content is missing. Capture ran before the app or images were ready. Wait for an application-ready selector and required images; handle image errors explicitly.
Navigation hangs at network idle. Polling, analytics, or persistent network connections prevent idleness. Use domcontentloaded plus a specific readiness selector or bounded delay.
Differences move between runs. Animation, live data, timestamps, random content, or unstable test state. Freeze test data, disable animations, or mask only the known volatile element.
Full-page images have different heights. Lazy content, different page state, or genuinely changed content length. Load below-fold content consistently and inspect whether the height change is the regression.
A resized image still shows the wrong responsive layout. Resizing changes pixels after rendering; it does not rerun responsive layout. Recapture the page at the target CSS viewport.
Baseline update hides an unexpected change. New output was accepted without review. Inspect the changed region and confirm intent before approving the baseline.

7. Performance, reliability, and cost

Capture only the routes and viewport sizes that answer the test question. Every additional browser, viewport, and state expands capture time and baseline review. Reuse a browser process in a test suite where appropriate, but isolate contexts when cookies or local storage could leak state between versions.

Reliability depends on making capture state deterministic: use stable test data, fixed locale and timezone, explicit readiness checks, and the same browser environment for baseline and comparison. Keep thresholds narrow and review changes before updating expected images.

Tool costs and usage rules change; consult the provider’s current plan documentation before building a large hosted matrix. Percy documents that each responsive width consumes a screenshot toward monthly usage. The research reviewed did not establish current plan prices for Chromatic or Percy, so this guide does not quote them.

Or skip the browser setup

ScreenshotNeo can capture a URL through one API request. This example saves the returned image; its output format can be selected in the API configuration. See the ScreenshotNeo API documentation for request options.

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 Bun.write('shot.webp', new Uint8Array(await res.arrayBuffer()));

Set the same viewport and page state in both requests if you are comparing versions. ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Visit ScreenshotNeo or sign up free for 1,000 screenshots a month with no card.

FAQ

Should I match screenshot file dimensions or browser viewport?

Match the CSS viewport used to render each page. File dimensions are an output of capture and do not establish which responsive layout the browser rendered.

Should every pixel diff fail a test?

Not necessarily. Rendering noise can create small differences, but tolerances should stay tight enough to reveal real changes. Inspect diffs instead of treating a threshold as approval.

Can I compare screenshots captured on different operating systems?

You can, but environment differences may contribute to the result. For regression checks, use the same OS and browser environment for the baseline and new capture.