Skip to content

Pixelmatch

pixelmatch is the Mapbox image-diff library at the core of most JS visual regression tooling — including Playwright's toHaveScreenshot(). These guides cover the algorithm, every option, and when to reach for a faster or different engine.

I need to... Guide Summary
Understand what pixelmatch is and when to use it directly vs through Playwright Overview Use pixelmatch directly when diffing arbitrary PNGs outside Playwright (custom scripts, Puppeteer, Selenium). For standard Playwright VR tests, pixelmatch runs under the hood automatically — read this guide to tune its thresholds correctly.
Understand the YIQ + anti-aliasing detection algorithm Algorithm Pixelmatch uses YIQ color space (luminance-weighted) to compare pixels perceptually, then skips pixels on anti-aliased edges by default. Setting includeAA true or threshold 0 does not mean raw byte comparison — the algorithm is always perceptual.
Tune the threshold option Threshold Lower threshold is stricter — it reduces the maximum allowed YIQ delta per pixel before flagging a diff. The pixelmatch library default is 0.1; Playwright overrides it to 0.2. Bump threshold only to absorb genuine env noise, never to silence flake.
Use includeAA, diffColor, diffMask, alpha Other Options Use diffColorAlt to distinguish additions from removals in the diff PNG, and diffMask to get a transparent-background overlay. Avoid includeAA true unless captures are pinned to identical rendering environments, and avoid alpha 1 which hides the diff overlay behind the original image.
Call the API from Node API Call pixelmatch(img1, img2, output, width, height, options) with decoded RGBA Uint8Array buffers — not PNG file bytes. Pass null as output for count-only mode. Returns an integer count of mismatched pixels.
Use the CLI for ad-hoc diffs CLI Run npx pixelmatch img1.png img2.png diff.png threshold in shell scripts. Exit code 0 means match, 66 means diffs detected (not an error), 64/65 are real failures. Colors, diffMask, and alpha are API-only — write a Node script for those.
Load PNG files into pixelmatch via pngjs or sharp Reading PNGs Use pngjs as the default PNG decoder — PNG.sync.read() returns an object with a .data RGBA Buffer that pixelmatch expects directly. For multi-format support (JPEG, WebP) or faster processing, use sharp with .raw().ensureAlpha() to guarantee the RGBA channel order.
Handle the same-size requirement Same-Size Constraint Pixelmatch requires both buffers to be identical dimensions — it has no resize, pad, or align logic. The API throws, the CLI exits 65, and Playwright fails the assertion outright. Fix capture conditions so images match natively; resizing introduces interpolation artifacts that produce false diffs.
Evaluate performance and when to switch to odiff Performance Pixelmatch is single-threaded pure JS — adequate for suites under 500 screenshots. For larger suites or 4K captures, odiff is ~6x faster via native SIMD. Never replace pixelmatch inside Playwright's toHaveScreenshot — its auto-retry stability is tied to it.
Understand how Playwright defaults differ from pixelmatch defaults Inside Playwright Playwright defaults to threshold 0.2 (pixelmatch's own default is 0.1) and adds two extra knobs — maxDiffPixels and maxDiffPixelRatio — on top of the raw pixel count. Without either extra knob configured, any non-zero diff fails the test.
Use pixelmatch in custom scripts outside Playwright Standalone Use Build standalone diff scripts with pngjs for PNG decoding and pixelmatch for the comparison. Always guard against dimension mismatches explicitly — pixelmatch throws. For Puppeteer or other captures, pin viewport and DPR consistently across baseline and current captures.
Know pixelmatch's limitations and when to switch Limitations & Alternatives Pixelmatch is RGBA-only, single-threaded, and has no SSIM or semantic awareness. Switch to odiff for speed, looks-same for cross-OS font tolerance (CIEDE2000), or dssim for compression-robust structural similarity. Never switch libraries to mask flake — fix the environment first.
Pick a threshold for a specific scenario Tuning Recipes Start with Playwright's default of 0.2 and stabilize the suite, then tighten incrementally toward 0.1. For cross-OS captures use 0.2–0.3; for animations and gradients prefer maxDiffPixelRatio over raising threshold. Document every non-default per-test threshold with a comment.