ScreenshotNeo

BlogHow-to

How to Compare Website Screenshots with BrowserCat for Visual Changes

Use Playwright with BrowserCat to capture repeatable website screenshots, then compare them with a separate image-diff step to find visual changes.

By the ScreenshotNeo team4 October 202610 min read

To compare website screenshots with BrowserCat, use Playwright to capture the page in BrowserCat’s managed browser, save a baseline and a new capture under the same conditions, then run a separate image comparison tool on the files. BrowserCat provides the browser runtime and Playwright controls it. The reviewed BrowserCat documentation describes screenshot capture and monitoring ideas, but does not establish a built-in pixel-diff or complete visual-regression product. Keep capture, comparison, and alerting as distinct steps.

This guide builds a small Node.js workflow: capture a screenshot, create a pixel-difference image, and fail a command when changed pixels exceed a threshold. You can run the same capture locally or through BrowserCat. The threshold is a project choice, not a BrowserCat feature.

1. Decide whether screenshot comparison fits

A screenshot comparison can catch layout shifts, missing images, color changes, and other changes visible in a rendered page. It is especially useful when the page relies on JavaScript or requires a login. If you only need to check a value such as a price or a piece of text, an API or ordinary HTTP request may be simpler. BrowserCat’s guide describes both kinds of checks and recommends browser automation for rendered content. BrowserCat’s browser automation guide

Stage What it does Typical owner
Capture Opens a page in a browser and saves its pixels. Playwright running locally or connected to BrowserCat
Compare Measures or visualizes the difference between two image files. A separate diff library, visual review, or test service
Alert and review Decides whether a difference should fail CI, open a review, or notify someone. Your test pipeline and team workflow

2. Make captures repeatable

A diff is only useful when the two captures represent the same page state. Keep the browser engine, viewport, device scale factor, route, authentication, test data, and capture timing consistent. Control or account for animation, rotating content, advertisements, personalization, timestamps, and asynchronous loading. These are general visual-testing practices; the cited BrowserCat docs do not promise an automatic noise filter or masking feature.

  • Use a stable test account and seed data for authenticated pages.
  • Wait for a meaningful page landmark, such as the main heading, rather than relying on an arbitrary short delay.
  • Disable animations through a test stylesheet or a page style override when motion is irrelevant.
  • Capture the same viewport and device scale factor on each run.
  • Store the route and capture settings alongside the baseline so future runs can reproduce them.
  • Review a diff in context before declaring a regression; some changes are expected content updates.

3. Install the capture and comparison dependencies

This example uses Playwright with Chromium and the pixelmatch and pngjs packages for a simple pixel-level comparison. BrowserCat recommends Playwright in its quick start. Install Node.js first, then create a project:

mkdir screenshot-diff
cd screenshot-diff
npm init -y
npm install playwright pixelmatch pngjs
npx playwright install chromium

Set your BrowserCat API key in an environment variable when you want cloud execution. Create a key in your BrowserCat account; never commit it to source control.

export BROWSERCAT_API_KEY='YOUR_API_KEY'

On Windows PowerShell, use $env:BROWSERCAT_API_KEY="YOUR_API_KEY". BrowserCat’s quick start uses a WebSocket endpoint and an Api-Key header. See the BrowserCat quick start.

4. Capture the baseline and current page with Playwright

Save this as capture.mjs. The same script accepts a URL and output filename. By default it launches local Chromium. Set BROWSERCAT_API_KEY to connect to BrowserCat instead. The fixed viewport, scale factor, reduced motion setting, landmark wait, and fonts-ready check make the sample more repeatable, though they cannot make inherently dynamic content deterministic.

import { chromium } from 'playwright';

const [url, output = 'current.png'] = process.argv.slice(2);
if (!url) {
  console.error('Usage: node capture.mjs https://example.com output.png');
  process.exit(2);
}

const browser = process.env.BROWSERCAT_API_KEY
  ? await chromium.connect('wss://api.browsercat.com/connect', {
      headers: { 'Api-Key': process.env.BROWSERCAT_API_KEY },
    })
  : await chromium.launch({ headless: true });

try {
  const context = await browser.newContext({
    viewport: { width: 1365, height: 900 },
    deviceScaleFactor: 1,
    reducedMotion: 'reduce',
  });
  const page = await context.newPage();
  await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
  await page.locator('body').waitFor({ state: 'visible', timeout: 30000 });
  await page.evaluate(() => document.fonts.ready);
  await page.screenshot({ path: output, fullPage: true, animations: 'disabled' });
  console.log(`Saved ${output}`);
  await context.close();
} finally {
  await browser.close();
}

Capture the reference page after approving it as the expected appearance, then capture the same URL again after a deployment or on a schedule:

node capture.mjs https://example.com baseline.png
# Later, after a change:
node capture.mjs https://example.com current.png

The BrowserCat connection switches the browser runtime; Playwright still performs navigation and screenshot capture. The quick start demonstrates replacing a local launch with chromium.connect at wss://api.browsercat.com/connect. BrowserCat currently documents Chromium and Chrome as running browser options; Firefox and WebKit are described as roadmap options in its configuration docs. Confirm current support before planning browser coverage. BrowserCat browser configuration

5. Compare the two PNG files

Save the following as compare.mjs. It requires equal image dimensions, creates a red-highlighted diff image, reports the changed-pixel ratio, and returns a failing exit code if the ratio exceeds the threshold you provide. It is a minimal pixel comparison: it does not understand page semantics, ignore dynamic regions, or decide whether a change is acceptable.

import fs from 'node:fs';
import { PNG } from 'pngjs';
import pixelmatch from 'pixelmatch';

const [baselinePath, currentPath, diffPath = 'diff.png', maxRatioArg = '0.001'] = process.argv.slice(2);
if (!baselinePath || !currentPath) {
  console.error('Usage: node compare.mjs baseline.png current.png [diff.png] [max-ratio]');
  process.exit(2);
}
const maxRatio = Number(maxRatioArg);
if (!Number.isFinite(maxRatio) || maxRatio < 0 || maxRatio > 1) {
  console.error('max-ratio must be a number from 0 to 1');
  process.exit(2);
}

const before = PNG.sync.read(fs.readFileSync(baselinePath));
const after = PNG.sync.read(fs.readFileSync(currentPath));
if (before.width !== after.width || before.height !== after.height) {
  console.error(`Image dimensions differ: ${before.width}x${before.height} vs ${after.width}x${after.height}`);
  process.exit(2);
}
const diff = new PNG({ width: before.width, height: before.height });
const changed = pixelmatch(before.data, after.data, diff.data, before.width, before.height, {
  threshold: 0.1,
  includeAA: false,
  diffColor: [255, 0, 0],
});
fs.writeFileSync(diffPath, PNG.sync.write(diff));
const total = before.width * before.height;
const ratio = changed / total;
console.log(`${changed}/${total} pixels changed (${(ratio * 100).toFixed(4)}%); diff: ${diffPath}`);
if (ratio > maxRatio) process.exit(1);

Run it with a chosen tolerance. Here, 0.001 means a failure above 0.1% changed pixels:

node compare.mjs baseline.png current.png diff.png 0.001

The library’s threshold controls per-pixel color sensitivity, while includeAA controls whether anti-aliased pixels are included. The ratio threshold is a separate project-level decision. Pixel metrics can be noisy across rendering environments; use the output image to inspect what changed.

6. Add it to CI and manage baselines

  1. Capture a baseline from a controlled environment and commit or store it as a versioned artifact.
  2. Capture the same route after the relevant deployment or pull request build.
  3. Compare the images and preserve the diff image when the command fails.
  4. Review the difference before approving a new baseline; do not automatically bless every changed output.
  5. Record the URL, viewport, browser choice, commit, and relevant test data with each result.

For multiple routes, keep a manifest of URL-to-baseline mappings and run one capture and comparison per route. Limit parallel sessions to what your CI capacity and BrowserCat account can support; the reviewed source material does not establish specific concurrency limits or prices. Avoid uploading authenticated screenshots or private page data to public build artifacts.

Pixel-level comparison is straightforward and produces an intuitive overlay, but small rendering changes can trigger it. Perceptual comparison can be less sensitive to small pixel shifts, while DOM-assisted methods can help target known elements or regions. These approaches have different tradeoffs, and the sources reviewed here do not compare vendors or define an automatic winner. Choose based on how much false-positive review your team can tolerate, viewport/browser coverage, baseline approval needs, CI environment, and execution cost.

7. BrowserCat configuration and practical limits

BrowserCat describes one browser connection endpoint for Playwright, Puppeteer, and other CDP clients. Its current product page says the router selects a managed backend based on capability, latency, and availability, and advertises automatic failover; the page identifies Cloudflare as live and additional backends as coming. These are current vendor statements, not independent verification. BrowserCat

The configuration docs describe two ways to pass options:

  • BrowserCat-Opts header: JSON configuration for browser selection, launch arguments, user preferences, and proxy settings.
  • Query parameters: a smaller configuration set, including browser selection. When both forms set a key, the header takes precedence.

Documented header options include browser, enableArgs, disableArgs, userPrefs, and proxy. The docs say sessions currently run near the request and explicit region routing is on the roadmap. If a CDP client cannot send headers, the docs describe query parameters for configuration and an apiKey query parameter for authentication, while warning to use secure wss/https transport. Prefer headers where available so credentials do not appear in URLs or logs. Check the configuration reference for current details.

Do not interpret managed browser availability or automatic failover as a guarantee that a screenshot will be identical across runs. Fonts, browser versions, geography, third-party content, and timing can all affect pixels. The reviewed docs describe capture and monitoring workflows but do not document an end-to-end visual test product, pixel-diff algorithm, masking feature, or alerting system.

Or skip the browser setup

If you only need an image or PDF from a URL, ScreenshotNeo is a website screenshot API and MCP server. Its one-call API returns a PNG, JPEG, WebP, or PDF; you can compare returned image files with your own diff step.

cURL:

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

Python:

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)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo API documentation for request options. Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. An 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.

Sign up free for 1,000 screenshots a month, with no card required.

Troubleshooting

Symptom Likely cause Fix
BrowserCat connection fails or closes immediately Missing or invalid API key, wrong endpoint, or authentication header mismatch. Use wss://api.browsercat.com/connect, verify the key, and send it as Api-Key. Keep it out of source control.
Navigation times out The page is slow, blocked, or still loading external resources. Check the URL and access requirements, use a meaningful readiness condition, and set a timeout appropriate to the site. Do not wait for network idle if long-lived requests prevent it.
Screenshot differs on every run Dynamic content, animation, asynchronous fonts/images, or varying test data. Stabilize data, wait for the content you need, disable animations, and mask or remove volatile page regions in your own capture workflow if appropriate.
Comparison exits with dimension mismatch Viewport, full-page height, scale factor, or page content height changed. Match context settings and page state. If the page height legitimately changes, compare stable sections or treat the layout change as a reviewable result.
Many harmless pixels fail the threshold Anti-aliasing, font rendering, or an overly strict ratio. Inspect diff.png, stabilize the rendering environment, adjust pixel sensitivity or the accepted ratio deliberately, and document the reason.
Configuration query does not take effect A header value overrides the same query parameter. Check both sources and remember that BrowserCat-Opts header keys take precedence.
API key appears in request logs Credentials were put in a URL query parameter. Use the API-key header when the client supports it; if query authentication is necessary, use wss/https and prevent URL logging.

Performance, reliability, and cost

Full-page screenshots take longer and produce larger files than viewport captures, especially on long pages. Capture only the routes and page areas that provide useful coverage, and avoid excessive concurrency. The BrowserCat quick start positions its cloud connection as a way to run scripts without consuming the local machine, but the reviewed sources provide no independent throughput figures, visual-diff benchmarks, or cost comparison. Check current BrowserCat account terms for your expected session volume.

For reliability, preserve the baseline and diff artifact, retry only transient capture failures, and distinguish a capture failure from a genuine visual difference. A failed navigation should not be silently treated as a passing comparison. BrowserCat advertises routing and failover on its product page, but a successful session does not remove the need for your own timeout handling and review process.

FAQ

Does BrowserCat compare screenshots automatically?

The reviewed docs establish browser connection and screenshot capture, plus monitoring use cases. They do not establish a built-in screenshot-diff feature.

Can I use BrowserCat for login-gated pages?

Playwright can automate page interactions, so the workflow can capture pages reached after login. Use a dedicated test account and protect screenshots and credentials.

Should I use screenshot diffs for every content change?

No. Use them where rendered appearance matters. For a simple text or numeric value that an API exposes, a direct data check may be easier to maintain.

What should I do when a diff appears?

Inspect the generated image, confirm whether the change is expected, and update the approved baseline only after review.