ScreenshotNeo

BlogHow-to

How to Fix Chromium Screenshots That Differ Between Local and CI Runs

Make Chromium screenshots reproducible across local and CI runs by aligning browser versions, operating environments, viewport settings, and capture timing.

By the ScreenshotNeo team4 October 20266 min read

To make Chromium screenshots match between local development and CI, capture and compare them in the same pinned environment. Align the Playwright version and its Chromium build, operating system and dependencies, viewport and device scale factor, and capture timing. Playwright warns that screenshots can vary with the host operating system, browser version, settings, hardware, power source, and headless mode, and recommends running in the environment used to generate the baseline. [Playwright visual comparisons]

1. Identify what differs

Before changing a baseline, establish whether local and CI are rendering under equivalent conditions. A pixel diff is evidence of a difference, not a diagnosis: the source may be a real UI change, a browser or platform mismatch, geometry, or timing.

  • Record the operating system or container image used for each run.
  • Record the Playwright package version and the browser binary version.
  • Compare the viewport dimensions and device scale factor.
  • Check whether one run is headed and the other headless, or whether different launch settings are applied.
  • Look at the diff to see whether it affects text edges, broad layout regions, or content that changes over time.

Do not infer a specific cause from the diff alone. The documentation identifies several possible sources, but the repository and CI configuration determine which apply.

2. Pin Playwright and Chromium

Use the project’s locked Playwright dependency in both environments, and launch the browser installed for that Playwright version. Avoid using a separately updated system Chromium on one side and Playwright-managed Chromium on the other.

npm ci
npx playwright install chromium
npx playwright --version

Commit the package lockfile and use the same installation commands locally and in CI. If CI caches downloaded browsers, include the Playwright version in the cache key. Otherwise, a cache can preserve browser binaries from an older dependency version. Playwright manages browser binaries and documents version-aware browser caching for CI. [Browser management] [Playwright CI]

3. Use one operating environment for baselines

Matching the Chromium version does not make macOS, Windows, and Linux rendering identical. Fonts, operating-system rendering, dependencies, hardware, power settings, and headless behavior can all affect output. For dependable visual baselines, generate and check snapshots in the same pinned CI image or container.

Playwright recommends using its CI container image or installing the required dependencies on the CI agent. The key is consistency: the baseline generator and the baseline checker should use the same environment. If the team intentionally checks snapshots on several platforms, use platform-specific baselines instead of expecting one image to match everywhere. [Visual comparisons] [CI setup]

4. Fix viewport and output scale

Set a fixed viewport and device scale factor rather than inheriting machine-specific defaults. In Playwright’s screenshot API, scale: "css" produces one output pixel per CSS pixel; scale: "device" produces one output pixel per device pixel and can make images larger on high-DPI configurations. Choose one and use it for both baseline creation and comparison. [Screenshot API]

import { test, expect } from '@playwright/test';

test.use({
  viewport: { width: 1280, height: 800 },
  deviceScaleFactor: 1,
});

test('dashboard visual baseline', async ({ page }) => {
  await page.goto('http://127.0.0.1:3000/dashboard');
  await expect(page).toHaveScreenshot('dashboard.png', {
    fullPage: true,
    scale: 'css',
  });
});

Keep these settings in the shared Playwright configuration or test setup so local and CI runs use the same values. If a project deliberately tests multiple viewport sizes or device scale factors, treat each configuration as its own baseline.

5. Stabilize capture timing

Pages can change while a screenshot is being taken because content, animation, or asynchronous loading is still in progress. Playwright’s toHaveScreenshot() assertion takes repeated screenshots and waits for two consecutive captures to match before comparing with the baseline. This helps with transient changes, but it cannot compensate for different browsers or operating systems. [Visual comparison behavior]

await page.goto('http://127.0.0.1:3000/dashboard');
await page.locator('[data-testid="dashboard-ready"]').waitFor();
await expect(page).toHaveScreenshot('dashboard.png', {
  animations: 'disabled',
});

Prefer waiting for a meaningful ready condition in the application over adding a long arbitrary delay. Disable animations when they are irrelevant to the visual assertion. If the page includes live data, clocks, rotating content, or randomized values, provide stable test data or control those values as part of the test setup.

6. Review diffs before updating snapshots

Open the image diff and decide whether it reflects an intended UI change or a mismatched environment. Update snapshots only after that review. Playwright supports intentional updates with --update-snapshots; using it to overwrite a baseline before diagnosing the change can hide a real regression. [Updating snapshots]

npx playwright test --update-snapshots

For teams whose baselines are tied to a particular platform, keep the platform context explicit in the snapshot workflow. Playwright supports platform-specific snapshot names because platforms, fonts, and rendering can differ. [Platform-specific snapshots]

7. A reproducible workflow

  1. Install dependencies from the committed lockfile.
  2. Install the Chromium build managed by that Playwright version.
  3. Run baseline generation and comparison in the same pinned CI image.
  4. Use fixed viewport and device scale settings.
  5. Wait for the page’s ready state and suppress irrelevant animation or changing data.
  6. Inspect image diffs before updating the baseline.

This workflow targets reproducibility. It does not guarantee identical pixels if the browser, host environment, fonts, hardware, or capture settings still differ.

8. Troubleshooting

Symptom Likely cause Fix
Text edges differ across machines Different operating systems, fonts, or rendering environment Generate and compare baselines in the same pinned CI/container environment.
CI changes after a dependency update Playwright or its managed browser version changed Use the committed lockfile, reinstall the managed browser, and key browser caches to the Playwright version.
Screenshot dimensions differ Viewport or device scale factor differs, or screenshot scale is device-based Set the same viewport and device scale factor; choose the same screenshot scale mode.
Only some runs fail with changing content Capture occurs while the page is still changing Wait for a stable application-specific condition and disable irrelevant animations or stabilize test data.
Baseline update makes the failure disappear The baseline may have been replaced without diagnosing the original diff Review the old and new images and confirm the change is intentional before accepting the update.
Local result differs despite matching package versions Host OS, browser installation path, launch mode, dependencies, or hardware may still differ Compare full environment details and run the visual check in the same pinned image used for baseline generation.

9. Performance, reliability, and cost

Running visual checks in a consistent container makes results easier to reproduce and reduces time spent investigating environment-only diffs. Browser installation and image caching can affect CI setup time; use a cache keyed to the Playwright version so speed does not come at the cost of silently reusing a mismatched browser. Repeated screenshot capture helps detect transient rendering, but adds work and cannot fix environmental differences. The cited Playwright guidance provides no universal pixel tolerance or failure-rate figure, so choose comparison thresholds based on the application and review actual diffs.

Or skip the browser setup

For capturing a page as an asset outside a test-runner baseline, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF; see the API documentation for its 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}`);
  • Cookie banners, popups, and chat widgets are removed before the screenshot; those steps can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers say the page verdict and whether the shot was billed.
  • An MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf.
  • 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000. Every feature is on every plan.

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

FAQ

Should I generate visual baselines on my laptop?

You can, if the comparison environment matches it. For teams with different developer operating systems, a shared pinned CI environment avoids relying on cross-platform pixel identity.

Does waiting for two matching screenshots fix platform differences?

No. It reduces failures from transient page changes, but it does not make different operating systems, browser builds, or rendering settings equivalent.

When should I update a snapshot?

After reviewing the diff and confirming the visual change is intentional and the environments are aligned.

Should every platform share one baseline?

Only if your workflow requires it and the rendered output is expected to match. Platform-specific snapshots are an option when platform rendering differences are intentional.