Skip to content
Latchkey

Playwright anti-aliasing / sub-pixel screenshot differences in CI

Anti-aliasing smooths edges by blending pixels, and the exact blend can vary run to run or between GPUs. Playwright fails a screenshot on a single differing pixel by default, so set a small maxDiffPixels and raise threshold to absorb this sub-pixel noise.

What this error means

Screenshot tests fail intermittently with a handful of differing pixels along curved or diagonal edges. Re-running sometimes passes. The diff shows faint fringing on borders and text, not a layout change.

Playwright
  Error: A snapshot doesn't match its reference.
    438 pixels (ratio 0.002 of all image pixels) are different.
    (fringe along rounded corners and text edges)

Common causes

Zero default tolerance on noisy renderers

With no maxDiffPixels set and a strict threshold, edges that anti-alias slightly differently exceed it and register as changed pixels. Shared CI runners vary in GPU and backend.

Unstable rendering: animations, carets, or scaling

A blinking caret, a mid-flight CSS animation, or a non-integer device scale factor produces different sub-pixel output on each capture.

How to fix it

Set tolerances and disable animations

Increase threshold, allow a small maxDiffPixels, and let Playwright freeze CSS animations during the capture.

tests/card.spec.ts
await expect(page).toHaveScreenshot('card.png', {
  threshold: 0.2,
  maxDiffPixels: 100,
  animations: 'disabled',
  caret: 'hide',
});

Set expect defaults once and pin device scale

Configure toHaveScreenshot options in the config so every screenshot shares a sane tolerance, and pin an integer deviceScaleFactor to avoid sub-pixel resampling.

playwright.config.ts
export default defineConfig({
  use: { deviceScaleFactor: 1 },
  expect: { toHaveScreenshot: { maxDiffPixels: 100, threshold: 0.2 } },
});

How to prevent it

  • Set animations: 'disabled' and caret: 'hide' for screenshot tests.
  • Configure maxDiffPixels/threshold globally, not per test.
  • Use a whole-number deviceScaleFactor.

Frequently asked questions

What causes "Anti-aliasing / sub-pixel noise"?
With no maxDiffPixels set and a strict threshold, edges that anti-alias slightly differently exceed it and register as changed pixels. Shared CI runners vary in GPU and backend.
How do I fix Anti-aliasing / sub-pixel noise?
Increase threshold, allow a small maxDiffPixels, and let Playwright freeze CSS animations during the capture.

Related guides

References

Latchkey auto-heals failures like this one - detected, fixed, and retried without you. Start free → 30-day trial · No credit card