The smallest and fastest pixel-level image comparison library with anti-aliasing detection.
npx @tessl/cli install tessl/npm-pixelmatch@7.1.0Pixelmatch is the smallest, simplest and fastest JavaScript pixel-level image comparison library, originally created to compare screenshots in tests. It features accurate anti-aliased pixels detection and perceptual color difference metrics with zero dependencies.
npm install pixelmatchindex.d.ts)import pixelmatch from 'pixelmatch';For CommonJS environments:
const pixelmatch = require('pixelmatch');import pixelmatch from 'pixelmatch';
import fs from 'fs';
import {PNG} from 'pngjs';
// Load images
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});
// Compare images
const numDiffPixels = pixelmatch(img1.data, img2.data, diff.data, width, height, {
threshold: 0.1
});
console.log(`Found ${numDiffPixels} different pixels`);
// Save diff image
fs.writeFileSync('diff.png', PNG.sync.write(diff));For browser environments using Canvas:
import pixelmatch from 'pixelmatch';
const img1 = img1Context.getImageData(0, 0, width, height);
const img2 = img2Context.getImageData(0, 0, width, height);
const diff = diffContext.createImageData(width, height);
const numDiffPixels = pixelmatch(img1.data, img2.data, diff.data, width, height, {
threshold: 0.1
});
diffContext.putImageData(diff, 0, 0);Core pixel-level image comparison functionality that compares two equally sized images and returns the number of mismatched pixels.
/**
* Compare two equally sized images, pixel by pixel.
* @param {Uint8Array | Uint8ClampedArray} img1 - First image data.
* @param {Uint8Array | Uint8ClampedArray} img2 - Second image data.
* @param {Uint8Array | Uint8ClampedArray | void} output - Image data to write the diff to, if provided.
* @param {number} width - Input images width.
* @param {number} height - Input images height.
* @param {PixelmatchOptions} [options] - Configuration options.
* @returns {number} The number of mismatched pixels.
*/
function pixelmatch(img1, img2, output, width, height, options = {});Usage Examples:
// Basic comparison without diff output
const differences = pixelmatch(img1.data, img2.data, null, 800, 600);
// Comparison with diff output and custom threshold
const differences = pixelmatch(
img1.data,
img2.data,
diff.data,
800,
600,
{ threshold: 0.2 }
);
// Comparison with anti-aliasing detection disabled
const differences = pixelmatch(
img1.data,
img2.data,
diff.data,
800,
600,
{ includeAA: true, threshold: 0.1 }
);
// Comparison with custom diff colors and transparency
const differences = pixelmatch(
img1.data,
img2.data,
diff.data,
800,
600,
{
threshold: 0.1,
alpha: 0.5,
aaColor: [0, 192, 0], // Green for anti-aliased pixels
diffColor: [255, 0, 255], // Magenta for different pixels
diffColorAlt: [0, 255, 0] // Green for dark-on-light differences
}
);
// Create a transparent diff mask
const differences = pixelmatch(
img1.data,
img2.data,
diff.data,
800,
600,
{ diffMask: true }
);Pixelmatch provides a command-line interface for comparing PNG images directly from the terminal.
pixelmatch image1.png image2.png [diff.png] [threshold] [includeAA]Parameters:
image1.png - Path to the first PNG imageimage2.png - Path to the second PNG imagediff.png (optional) - Path where the diff image will be savedthreshold (optional) - Matching threshold as a number (0 to 1)includeAA (optional) - Whether to include anti-aliasing detection ("true" or "false")Exit Codes:
0 - Success, no differences found64 - Invalid arguments (insufficient parameters)65 - Image dimensions do not match66 - Differences found between imagesUsage Examples:
# Basic comparison
pixelmatch before.png after.png
# Generate diff image with default settings
pixelmatch before.png after.png diff.png
# Custom threshold (more sensitive)
pixelmatch before.png after.png diff.png 0.05
# Disable anti-aliasing detection
pixelmatch before.png after.png diff.png 0.1 trueCLI Output Format:
The command-line interface provides performance timing, difference count, and error percentage:
matched in: 15.123ms
different pixels: 143
error: 0.15%/**
* Configuration options for pixelmatch comparison
*/
interface PixelmatchOptions {
/** Matching threshold (0 to 1). Smaller values make comparison more sensitive. Default: 0.1 */
threshold?: number;
/** If true, disables detecting and ignoring anti-aliased pixels. Default: false */
includeAA?: boolean;
/** Blending factor of unchanged pixels in diff output (0 to 1). Default: 0.1 */
alpha?: number;
/** Color of anti-aliased pixels in diff output [R, G, B]. Default: [255, 255, 0] */
aaColor?: [number, number, number];
/** Color of differing pixels in diff output [R, G, B]. Default: [255, 0, 0] */
diffColor?: [number, number, number];
/** Alternative color for dark-on-light differences [R, G, B]. If not provided, uses diffColor. Default: undefined */
diffColorAlt?: [number, number, number];
/** Draw diff over transparent background (mask) rather than original image. Default: false */
diffMask?: boolean;
}Pixelmatch throws errors for the following conditions:
// Invalid image data format
throw new Error('Image data: Uint8Array, Uint8ClampedArray or Buffer expected.');
// Mismatched image dimensions
throw new Error('Image sizes do not match.');
// Image data doesn't match specified width/height
throw new Error('Image data size does not match width/height.');Error Handling Examples:
try {
const differences = pixelmatch(img1.data, img2.data, diff.data, width, height);
console.log(`Found ${differences} different pixels`);
} catch (error) {
if (error.message.includes('Image sizes do not match')) {
console.error('Images must have the same dimensions');
} else if (error.message.includes('Image data size does not match')) {
console.error('Image data length doesn\'t match width × height × 4');
} else if (error.message.includes('Uint8Array')) {
console.error('Invalid image data format - must be Uint8Array or Uint8ClampedArray');
} else {
console.error('Unexpected error:', error.message);
}
}Pixelmatch implements advanced anti-aliasing detection based on the "Anti-aliased Pixel and Intensity Slope Detector" paper by V. Vysniauskas (2009). This feature automatically identifies and handles anti-aliased pixels to reduce false positives in image comparisons.
// Enable anti-aliasing detection (default behavior)
const differences = pixelmatch(img1.data, img2.data, diff.data, width, height, {
includeAA: false // Anti-aliased pixels are ignored in diff count
});
// Disable anti-aliasing detection to count all pixel differences
const differences = pixelmatch(img1.data, img2.data, diff.data, width, height, {
includeAA: true // Anti-aliased pixels are counted as differences
});The library uses perceptual color difference calculation based on the YIQ NTSC transmission color space, implementing research from "Measuring perceived color difference using YIQ NTSC transmission color space in mobile applications" by Y. Kotsarenko and F. Ramos (2010).
YIQ Color Space Implementation:
0.5053 * y² + 0.299 * i² + 0.1957 * q²Alpha Blending for Transparent Pixels: When comparing images with transparency, pixelmatch uses a sophisticated background pattern:
48 + 159 * (position % 2)48 + 159 * ((position / φ | 0) % 2) where φ = 1.61803398874989548 + 159 * ((position / (φ + 1) | 0) % 2)Pixelmatch includes an optimized fast path for identical images:
Uint32Array views for 32-bit integer comparison instead of byte-by-byteThe library implements several key internal algorithms:
Anti-aliasing Detection (antialiased):
Color Delta Calculation (colorDelta):
Pixel Validation (isPixelData):
ArrayBuffer.isView() and BYTES_PER_ELEMENT === 1 checksPixelmatch provides extensive customization for visualizing differences:
// Custom color scheme for medical imaging
const differences = pixelmatch(img1.data, img2.data, diff.data, width, height, {
threshold: 0.05,
alpha: 0.2, // More transparent background
aaColor: [0, 255, 0], // Green for anti-aliased pixels
diffColor: [255, 0, 0], // Red for general differences
diffColorAlt: [0, 0, 255] // Blue for dark-on-light differences
});
// Create a binary mask showing only differences
const differences = pixelmatch(img1.data, img2.data, diff.data, width, height, {
diffMask: true, // Transparent background
alpha: 0, // Fully transparent unchanged pixels
diffColor: [255, 255, 255] // White differences
});Performance Optimizations:
Fast Path for Identical Images:
Uint32Array views to compare 4 pixels at once (32-bit chunks)Efficient Memory Access:
Algorithmic Efficiency:
Typical Performance: