Library / SDK
mapbox/pixelmatch avatar
mapbox/pixelmatch

pixelmatch: a dependency-light pixel diff for screenshot tests

The smallest, simplest and fastest JavaScript pixel-level image comparison library

6,968 stars329 forksJavaScriptISC

At a glance

What is it?
pixelmatch compares two images pixel by pixel, returns a mismatch count and can write a diff image. It is a library, not a test runner, and the README is explicit about what it does not do.
Who is it for?
Adopt pixelmatch when you already produce two images of equal dimensions and want a mismatch count plus a diff image inside your own test harness. Do not adopt it if you need cross-browser screenshot capture, baseline storage or a test runner; it supplies none of those.
Can I use it commercially?
Yes. ISC is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 14 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What pixelmatch solves, and for whom

The README frames the origin plainly: the library was "originally created to compare screenshots in tests." That sentence defines the audience. If you capture a rendered page or a canvas twice and want a machine-readable answer to "did anything change", pixelmatch gives you that answer as a number and, optionally, as an image.

The scope is deliberately narrow. It does not drive a browser, does not store baselines and does not know what a test is. It receives two typed arrays of pixel data plus a width and height, and it returns the number of mismatched pixels. Everything around that call (capture, storage, reporting, pass or fail) belongs to your harness.

That narrowness is the reason the package has no dependencies in the library itself. The README states it is "just a few hundred lines of code" with "no dependencies", operating on "raw typed arrays". The npm manifest tells a slightly more nuanced story: pngjs appears under dependencies, but it is there for the CLI binary, not for the comparison function. The library entry point works on buffers you already hold.

How the comparison actually runs

The API is one function with five positional arguments and an options object. The README documents the signature as pixelmatch(img1, img2, output, width, height[, options]). The first two arguments are the images to compare, output is where the diff image is written (or null if you do not want one), and width and height describe the images.

The constraint that bites first is stated twice in the README: image dimensions must be equal, and all three images need to have the same dimensions. There is no scaling, no alignment and no cropping. If your two captures differ by a scrollbar or a shifted layout, the comparison is meaningless before it starts.

The return value is the number of mismatched pixels. Internally the library works through the option set: threshold controls sensitivity, includeAA toggles anti-aliased pixel detection, and the color options control how the diff is painted. The README credits three sources for the method, including the OKLab color space for perceptual color difference and the OKLab HyAB metric for very large color differences, plus a 2009 anti-aliased pixel and intensity slope detector. Those citations matter because they explain why a pixel that differs only slightly in color may not be counted, and why anti-aliased edges are ignored by default.

The README also documents a second mode. With windowSize set to a finite N, the return value stops being a total and becomes "the maximum number of differing pixels in any N by N sliding window". The stated motivation is noise: GPU dithering and sub-pixel anti-aliasing scatter stray pixels, which never fill a small square, while a real regression changes a compact area. One detail is easy to miss: N never exceeds either image dimension, so on a 10 by 2 image, windowSize 32 yields a 2 by 2 window. The default is Infinity, which makes the window the whole image, which is the total count.

Install and a first real comparison

Install from npm. The README gives the command as npm install pixelmatch. Note that the package is published as an ES module ("type": "module" in package.json), so the import syntax below is the expected one.

bash
npm install pixelmatch

The README's Node example pairs pixelmatch with pngjs to read and write PNG files. The shape is: read both PNGs, take width and height from the first, allocate a diff PNG, call pixelmatch, then write the diff.

js
import fs from 'fs';
import {PNG} from 'pngjs';
import pixelmatch from 'pixelmatch';

const img1 = PNG.sync.read(fs.readFileSync('img1.png'));
const img2 = PNG.sync.read(fs.readFileSync('img2.png'));
const {width, height} = img1;
const diff = new PNG({width, height});

pixelmatch(img1.data, img2.data, diff.data, width, height, {threshold: 0.1});

fs.writeFileSync('diff.png', PNG.sync.write(diff));

The README's one-line form shows the same call with the diff buffer passed directly and the option object inline, returning the count:

js
const numDiffPixels = pixelmatch(img1, img2, diff, 800, 600, {threshold: 0.1});

If you would rather not write any JavaScript, the package ships a binary. The README documents it as working with PNG images and taking the two inputs, an output path and the threshold as positional arguments:

bash
pixelmatch image1.png image2.png output.png 0.1

After any of these calls, the diff image uses red for differing pixels by default and yellow for anti-aliased pixels, blended over the original at an alpha of 0.1. If you only want the number, pass null as the output argument and skip the file write.

Where pixelmatch is the wrong tool

The equal-dimensions rule is the hardest limitation. Any pipeline that produces differently sized captures has to normalise them before pixelmatch sees them, and the README offers no helper for that.

The second limitation is semantic. A mismatch count cannot tell you whether the change is a bug. A moved button, a new cookie banner and a genuine rendering regression all produce nonzero counts. pixelmatch narrows the noise with anti-aliased pixel detection and, if you opt in, windowed counts, but the judgement stays with you.

The third limitation is that the library is not a test framework. The README documents no baseline management, no image storage, no retry logic and no reporter. If you want "run this suite across browsers and fail the build", pixelmatch is one function inside a larger system you still have to build or buy.

Finally, the PNG path depends on pngjs. The library function takes typed arrays, but the CLI and the README's Node example both go through PNG decoding. If your images are in another format, that decoding is your problem, not the library's.

Compared with Resemble.js and Blink-diff

The README names two inspirations directly: Resemble.js and Blink-diff. The stated difference is not accuracy but surface area. pixelmatch is described as a few hundred lines with no dependencies, working on raw typed arrays, so it can run in Node and in browsers.

That difference has practical consequences. Resemble.js and Blink-diff are built around higher-level workflows: they take images and produce reports, and they carry the machinery for that. pixelmatch gives you the comparison and the diff buffer, and nothing else. If you want a report, you write it. If you want to run the same comparison in a browser as part of a page-level check, the typed-array interface is what makes that possible, since the README's browser example reads two ImageData objects and writes a third via putImageData.

The trade-off is real in both directions. A library that does one thing will not guess what you meant when two images have different dimensions; a framework might crop or scale and hide the problem. With pixelmatch, that failure is loud.

Maintenance, releases and the ISC licence

The repository is not archived, and the last push was on 2026-09-15. The most recent release listed is v7.2.0 on 2026-04-29, preceded by v7.1.1 on 2026-04-28 and v7.1.0 on 2025-02-21. The gap between v7.1.0 and v7.1.1 is over a year, which suggests a project that releases when there is something to release rather than on a schedule. The README links to the releases page as the changelog, so upgrade notes live there rather than in the repository.

On cost: the library has no runtime dependencies, so an upgrade does not pull a tree of transitive packages. The CLI's pngjs dependency is the exception. The test script runs eslint via a pretest hook, then tsc and node --test, which means type definitions are checked as part of the suite; index.d.ts is published in the files list, so TypeScript consumers get types from the package itself.

Licensing is ISC, a permissive licence. The practical implication is that you can use pixelmatch in closed-source products without a copyleft obligation, but this is a description of the licence identifier, not legal advice. If your organisation has a policy list of approved licences, ISC is normally on it, and the LICENSE file is at the repository root.

Editorial conclusion

Adopt pixelmatch when you already produce two images of equal dimensions and want a mismatch count plus a diff image inside your own test harness. Do not adopt it if you need cross-browser screenshot capture, baseline storage or a test runner; it supplies none of those. Before wiring it into CI, verify that your two inputs really share width and height, and pick a threshold and, if GPU noise is a problem, a windowSize value against your own fixtures.

Frequently asked questions

What is pixelmatch and what does it do?

pixelmatch is a JavaScript pixel-level image comparison library, originally created to compare screenshots in tests. It takes two images as raw typed arrays plus a width and height, writes an optional diff image, and returns the number of mismatched pixels.

How do I install pixelmatch?

The README gives the npm command npm install pixelmatch. For browser use it also documents importing directly from a CDN with a module script pointing at https://esm.run/pixelmatch.

What does the pixelmatch threshold option control?

The README describes threshold as the matching threshold, ranging from 0 to 1, where smaller values make the comparison more sensitive. Its default is 0.1.

Is there a pixelmatch alternative I should consider?

The README names Resemble.js and Blink-diff as inspirations and contrasts itself with them: pixelmatch is a few hundred lines with no dependencies working on raw typed arrays, while those libraries are built around higher-level image comparison workflows.

Why does pixelmatch report no differences when my images look different?

The README states that anti-aliased pixels are detected and ignored by default, and that perceptual color difference metrics are used for comparison. Setting includeAA to true disables the anti-aliased pixel handling, and lowering threshold makes the comparison more sensitive.

Official sources

  1. Issues
  2. License: ISC
  3. mapbox/pixelmatch on GitHub
  4. README
  5. Releases
For maintainers

Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/mapbox-pixelmatch.svg)](https://hysenlabs.com/projects/mapbox-pixelmatch)
Community notes

Community notes