A fast, accurate, and configurable pixel-level image comparison library for Node.js, written in TypeScript. This is a drop-in replacement for the original pixelmatch library, maintaining full API compatibility while adding new features and improvements.
npm install @sitepager/pixel-matchThe package includes a command-line interface for comparing images directly from your terminal.
npx @sitepager/pixel-match <image1> <image2> [options]-o, --output <diff>: Output diff PNG image path-t, --threshold <number>: Matching threshold (0-1)--include-aa: Detect and ignore anti-aliased pixels--no-include-aa: Do not ignore anti-aliased pixels--horizontal-shift <pixels>: Horizontal shift in pixels--vertical-shift <pixels>: Vertical shift in pixels--alpha <number>: Alpha value for diff mask (0-1)--diff-mask: Output only the diff mask--help-options: Show all pixelmatch options and exit
Basic comparison with diff output:
npx @sitepager/pixel-match before.png after.png --output diff.pngWith custom threshold:
npx @sitepager/pixel-match before.png after.png --output diff.png --threshold 0.05With threshold and anti-aliasing detection:
npx @sitepager/pixel-match before.png after.png --output diff.png --threshold 0.05 --include-aaWith threshold, anti-aliasing, and pixel shift tolerance:
npx @sitepager/pixel-match before.png after.png --output diff.png --threshold 0.05 --include-aa --horizontal-shift 7 --vertical-shift 6With diff mask and custom alpha:
npx @sitepager/pixel-match before.png after.png --output diff.png --diff-mask --alpha 0.5The CLI will output:
- The time taken to perform the comparison
- The number of different pixels found
- The percentage of different pixels
- A diff image if an output path is specified
0: Images match (no differences found)1: Failed to read image12: Image dimensions do not match3: Error running pixelmatch64: Invalid usage (missing required arguments)65: Image dimensions do not match66: Images differ (differences found)
import pixelmatch from '@sitepager/pixel-match';
const diff = pixelmatch(
img1: Uint8Array | Uint8ClampedArray,
img2: Uint8Array | Uint8ClampedArray,
output: Uint8Array | Uint8ClampedArray | null,
width: number,
height: number,
options?: PixelmatchOptions
): numberimg1: First image data as a Uint8Array or Uint8ClampedArray in RGBA formatimg2: Second image data as a Uint8Array or Uint8ClampedArray in RGBA formatoutput: Optional output image data to write the diff to. If null, only the difference count is returnedwidth: Width of the images in pixelsheight: Height of the images in pixelsoptions: Configuration options for the comparison (see below)
The number of different pixels found between the images.
The library provides two different entry points for browser and Node.js environments:
import pixelmatch from '@sitepager/pixel-match/browser';
// Example with canvas elements
const canvas1 = document.getElementById('canvas1') as HTMLCanvasElement;
const canvas2 = document.getElementById('canvas2') as HTMLCanvasElement;
const outputCanvas = document.getElementById('output') as HTMLCanvasElement;
const ctx1 = canvas1.getContext('2d')!;
const ctx2 = canvas2.getContext('2d')!;
const outputCtx = outputCanvas.getContext('2d')!;
const img1 = ctx1.getImageData(0, 0, width, height);
const img2 = ctx2.getImageData(0, 0, width, height);
const output = outputCtx.createImageData(width, height);
const diff = pixelmatch(img1.data, img2.data, output.data, width, height, {
threshold: 0.1,
});
outputCtx.putImageData(output, 0, 0);import pixelmatch from '@sitepager/pixel-match/node';
import { readFileSync } from 'fs';
import { PNG } from 'pngjs';
// Read images
const img1 = PNG.sync.read(readFileSync('before.png'));
const img2 = PNG.sync.read(readFileSync('after.png'));
const { width, height } = img1;
const output = new PNG({ width, height });
// Compare images
const diff = await pixelmatch(
img1.data,
img2.data,
output.data,
width,
height,
{ threshold: 0.1 },
);
// Save diff image
output.pack().pipe(createWriteStream('diff.png'));-
Browser Pattern (
@sitepager/pixel-match/browser):- Use when working with canvas elements in a web browser
- Ideal for real-time image comparison in web applications
- Works with
ImageDataobjects from canvas contexts - Synchronous operation (no async/await needed)
- Best for client-side visual diff tools or image processing applications
-
Node.js Pattern (
@sitepager/pixel-match/node):- Use when working with image files on the server
- Ideal for automated testing, CI/CD pipelines, or server-side image processing
- Works with raw image data from file systems
- Asynchronous operation (requires async/await)
- Best for automated visual regression testing or server-side image processing
interface PixelmatchOptions {
threshold?: number; // Color difference threshold (0 to 1). Default: 0.1
includeAA?: boolean; // Whether to detect and ignore anti-aliasing. Default: false
alpha?: number; // Opacity of unchanged pixels in the diff output. Default: 0.1
aaColor?: [number, number, number]; // Color of anti-aliased pixels in the diff output [r, g, b]. Default: [255, 255, 0] (yellow)
diffColor?: [number, number, number]; // Color of different pixels in the diff output [r, g, b]. Default: [255, 0, 0] (red)
diffColorAlt?: [number, number, number]; // Alternative color for different pixels when img2 is darker [r, g, b]
diffMask?: boolean; // Whether to draw the diff over a transparent background (true) or over the original image (false)
horizontalShiftPixels?: number; // Number of pixels to check horizontally for similar pixels. Default: 0
verticalShiftPixels?: number; // Number of pixels to check vertically for similar pixels. Default: 0
}When no options are provided, the following defaults are used:
const defaultOptions = {
threshold: 0.1, // 10% color difference threshold
includeAA: false, // Don't detect anti-aliasing
alpha: 0.1, // 10% opacity for unchanged pixels
aaColor: [255, 255, 0], // Yellow for anti-aliased pixels
diffColor: [255, 0, 0], // Red for different pixels
diffColorAlt: undefined, // No alternative color
diffMask: false, // Draw diff over original image
horizontalShiftPixels: 0, // No horizontal shift
verticalShiftPixels: 0, // No vertical shift
};import { pixelmatch } from '@sitepager/pixel-match/browser';
const diff = pixelmatch(img1, img2, output, width, height);const options = {
threshold: 0.05, // More strict comparison
includeAA: true, // Include anti-aliasing in comparison
alpha: 0.5, // More visible unchanged pixels
diffColor: [255, 0, 255], // Purple diff color
diffMask: true, // Transparent background
};
const diff = pixelmatch(img1, img2, output, width, height, options);const options = {
horizontalShiftPixels: 7, // Allow 7px horizontal shift
verticalShiftPixels: 6, // Allow 6px vertical shift
threshold: 0.1,
};
const diff = pixelmatch(img1, img2, output, width, height, options);const options = {
aaColor: [0, 255, 255], // Cyan for anti-aliased pixels
};
const diff = pixelmatch(img1, img2, output, width, height, options);const options = {
diffColor: [255, 0, 0], // Red for differences (default)
diffColorAlt: [0, 0, 255], // Blue when img2 is darker
};
const diff = pixelmatch(img1, img2, output, width, height, options);const options = {
threshold: 0.07, // Color difference threshold
includeAA: true, // Detect and ignore anti-aliasing
alpha: 0.3, // Opacity of unchanged pixels in diff
aaColor: [0, 255, 255], // Cyan for anti-aliased pixels
diffColor: [255, 0, 0], // Red for differences
diffColorAlt: [0, 0, 255], // Blue for darker pixels in img2
diffMask: true, // Output only the diff mask
horizontalShiftPixels: 2, // Allow 2px horizontal shift
verticalShiftPixels: 2, // Allow 2px vertical shift
};
const diff = pixelmatch(img1, img2, output, width, height, options);
⚠️ Beta Feature Warning: ThehorizontalShiftPixelsandverticalShiftPixelsoptions are currently in beta. These features can significantly impact performance as they require additional pixel comparisons. Use with caution in production environments. We are actively working on performance optimizations for these features in an upcoming release.
The library will throw errors in the following cases:
-
Invalid Image Data Format
// Error: "Image data: Uint8Array, Uint8ClampedArray or Buffer expected" pixelmatch(regularArray, img2, null, width, height);
-
Mismatched Image Sizes
// Error: "Image sizes do not match" pixelmatch(img1, img2, null, width1, height1);
-
Invalid Dimensions
// Error: "Image data size does not match width/height" pixelmatch(img1, img2, null, width, height);
- Image Format: Always ensure your images are in RGBA format (4 bytes per pixel)
- Threshold Selection:
- Use lower thresholds (0.05-0.1) for precise comparisons
- Use higher thresholds (0.2-0.3) for more lenient comparisons
- Anti-aliasing: Set
includeAA: truewhen comparing screenshots with text or UI elements - Image Shifts:
⚠️ UsehorizontalShiftPixelsandverticalShiftPixelswith caution as they are in beta- Consider using these features only in development/testing environments
- For production use, consider preprocessing images to align them before comparison
npm run build— Build the package with tsupnpm test— Run tests with vitestnpm run format— Format code with Prettiernpm run changeset— Manage versioning and changelogs
Contributions are welcome! Please open issues or pull requests.
MIT