Why Do Playwright Screenshot Tests Look Different on My Computer and CI?
Local and CI screenshots can differ because the page renders in different environments. Compare versions, operating systems, fonts, and capture settings to find the cause.
Short answer: Playwright screenshots depend on the rendering environment as well as the page. Your computer and CI runner may use different operating systems, browser or Playwright versions, fonts, browser settings, headless modes, system dependencies, or screenshot scales. The most reliable fix is to generate baselines and compare them in the same pinned environment. Playwright recommends using the same environment for both.
1. Why the same page can produce different pixels
A screenshot is the result of browser rendering, not just your HTML and CSS. The host OS, OS version and settings, hardware, power source, headless mode, browser, and installed fonts can all affect the output. Browser and platform differences can change text rasterization, font fallback, spacing, and layout.
Playwright’s default snapshot names include the browser and platform. When a configuration has multiple projects, project names can also distinguish snapshots. That helps keep platform-specific baselines separate; it does not make output from different platforms pixel-identical. Playwright visual comparisons and Microsoft’s Playwright Workspaces guidance explain these platform-specific baselines.
2. Diagnose local-versus-CI differences
- Compare Playwright and browser versions. Check the installed
@playwright/testversion and browser binaries in both places. Install browsers using the matching Playwright version rather than relying on a separately installed system browser. - Compare operating systems and dependencies. Record the OS image, installed system packages, and fonts. On Linux, install Playwright’s browser system dependencies in CI. Playwright notes these OS dependencies cannot be cached like browser binaries; if you cache browser binaries, key the cache to the Playwright version.
- Compare browser projects and execution mode. Make sure local and CI run the same browser project and headless/headed mode. Playwright browsers run headless by default in its CI guidance.
- Compare fonts and font readiness. Check that the same local fonts exist in both environments and that intended web fonts have loaded before capture. A fallback font can change line wrapping and element dimensions.
- Compare viewport and screenshot scale. Match project viewport, device scale factor, and screenshot options. CSS scale produces one image pixel per CSS pixel; device scale captures physical device pixels and can create a larger high-DPI image.
- Separate timing noise from environment differences. Wait for the page’s meaningful ready state. If only timestamps, avatars, or other known volatile regions differ, mask those regions or use screenshot-only styles. Do not loosen tolerances so far that a real layout regression disappears.
- Choose a baseline strategy. For a deterministic CI gate, create and compare snapshots in one pinned environment. If cross-platform appearance is part of coverage, retain distinct baselines by OS and browser project.
3. Make CI use a stable Playwright environment
Playwright recommends consistent environments for visual comparisons and documents using containers in CI. Pin the container image to the Playwright version your project uses; the official CI guide shows versioned Playwright images. Run baseline generation and screenshot comparison in that same image and configuration. Playwright’s CI guide covers browser installation and container use.
For a project using Playwright Test, keep the project configuration explicit so the viewport and browser project are reviewable:
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
browserName: 'chromium',
headless: true,
viewport: { width: 1280, height: 720 },
deviceScaleFactor: 1,
},
projects: [
{ name: 'chromium-linux' },
],
});
Use an equivalent OS image and configuration when updating snapshots. The example fixes the project’s browser and geometry; the pinned CI image and matching local environment are what keep the rendering runtime aligned.
4. Stabilize capture-time variation with Playwright
toHaveScreenshot() waits until two consecutive screenshots match. Playwright also disables animations and hides the caret by default for screenshot assertions. These defaults help with capture-time noise, but they cannot eliminate genuine rendering differences between different operating systems or browser builds. See the PageAssertions API for the current assertion options.
import { test, expect } from '@playwright/test';
test('product page visual baseline', async ({ page }) => {
await page.goto('https://example.com/product');
await page.evaluate(() => document.fonts.ready);
await expect(page).toHaveScreenshot('product-page.png', {
fullPage: true,
animations: 'disabled',
caret: 'hide',
scale: 'css',
mask: [page.locator('[data-visual-test="timestamp"]')],
});
});
Replace the example URL and mask selector with your page and known dynamic region. Avoid masking large parts of the page: a mask is appropriate for a specific volatile element, not for hiding layout changes.
Useful screenshot assertion options
| Option | Use | Watch for |
|---|---|---|
fullPage |
Capture the full scrollable page. | Content loaded only during scrolling may need deliberate readiness handling. |
mask |
Cover known dynamic locators in the screenshot. | Mask only the changing region so meaningful differences remain visible. |
stylePath |
Apply screenshot-only CSS, for example to hide volatile content. | Keep these styles scoped to capture; broad rules can conceal regressions. |
scale: 'css' |
Produce one screenshot pixel per CSS pixel. | Image dimensions differ from device scale on high-DPI setups. |
scale: 'device' |
Capture at device pixels. | High device scale factors create larger images and different dimensions. |
threshold, maxDiffPixels, maxDiffPixelRatio |
Allow a defined amount of pixel or color difference. | Excessive tolerance can let actual visual regressions pass. |
animations, caret |
Control animation and caret behavior for the assertion. | These address capture noise, not cross-platform rendering. |
5. When you intentionally test multiple platforms
Cross-platform visual coverage should compare each intended browser and OS against its own baseline. Use separate Playwright projects and snapshot paths or names that make the platform context clear. Review differences as platform-specific output; do not expect screenshots from different host operating systems to match pixel for pixel.
If using remote browsers through Playwright Workspaces while the local host runs another OS, Microsoft’s guidance describes configuring a distinct service setup or OS-specific snapshot path. Keep browser engine, operating system, Playwright version, and capture geometry as explicit comparison axes.
6. Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Text wraps differently only in CI | A missing font or web font not loaded before capture. | Install matching fonts and wait for document.fonts.ready or the page’s font-ready condition. |
| Many pixels shift after a dependency update | Playwright or browser binary versions differ from those used to make the baseline. | Install browsers for the locked Playwright version; regenerate baselines intentionally in the pinned environment if upgrading. |
| CI reports missing browser libraries | Linux system dependencies are absent from the runner image. | Install Playwright browser dependencies or use the documented versioned Playwright container. |
| Snapshot dimensions differ | Viewport, device scale factor, full-page setting, or screenshot scale differs. | Match project viewport and device scale factor; use the same scale setting. |
| Only animated or personalized content fails | Capture happens before stable content, or content varies per run. | Wait for a meaningful ready state; mask a narrowly scoped volatile element or use screenshot-only styles. |
| Local baseline path does not match remote-browser output | The baseline path encodes the local host platform while the remote browser uses another OS. | Use platform-specific snapshots or configure the remote comparison’s OS-specific path. |
| Increasing tolerance makes failures disappear | The threshold is hiding an environment mismatch or real visual change. | Fix the runtime mismatch first, then set only a small justified tolerance for unavoidable pixel noise. |
7. Performance, reliability, and cost
Visual assertions need stable page readiness and consistent browser startup; repeated environment setup and mismatched browser downloads can add CI work or cause failures. A versioned container makes the runtime easier to reproduce, while browser-binary caches should be keyed to the Playwright version. Linux system dependencies still need to be present in the image.
Do not treat retries as a rendering fix: they can make intermittent timing failures less visible without correcting different fonts, OS settings, or browser versions. A screenshot comparison service may suit teams that want managed visual review, but the cited research does not establish pricing or performance for any specific service. Playwright itself is the direct route when the requirement is a deterministic test in your existing suite.
8. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server for developers. One GET request returns a PNG, JPEG, WebP, or PDF. For a reference capture, this cURL request saves a WebP:
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com/product \
-o shot.webp
See the ScreenshotNeo API documentation for request options. The API can remove cookie and consent banners, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. A screenshot API is useful for captures and workflows, while Playwright assertions remain the right fit when you need to compare application output to committed test baselines.
Sign up for ScreenshotNeo’s free plan: 1,000 screenshots a month, no card required.
9. FAQ
Should I commit screenshots generated on my laptop?
Only if the comparison environment reproduces the same rendering context. Otherwise, generate or update baselines in the pinned environment used for comparison.
Can Playwright make Windows and Linux screenshots identical?
No. It can make capture behavior more consistent, but platform rendering and fonts can still differ. Keep distinct baselines when both platforms are intentional targets.
Should I use pixel-difference tolerance to fix CI failures?
Use tolerance only for small, understood pixel-level variation after aligning environments. It is not a substitute for matching versions, fonts, geometry, and runtime.


