Pbd is a Pixel Buffer Diff library designed for visual regression tests. With zero dependencies and a bundle size under 2kb, Pbd works as a drop-in replacement for Pixelmatch and runs 8-10x faster.
Unlike other image diffing library that only show which pixels changed, Pbd shows if pixel changes were actually added or removed according the brightness theme of the baseline image, and represent the pixel changes in a way similar to code changes in a Pull Request to provide more context for each pixel change and make the view more easily actionable.
Additionaly, Pbd can detect changes visible accross multiple images, and overlay a low resolution highlight to help spot isolated pixel changes.
Update your package.json, and import or require statements, to save significant time and money on your visual regression pipeline and approval workflow.
const pbdDiffResult = diff(baselinePixelBuffer, candidatePixelBuffer, diffPixelBuffer, width, height, options);Pbd samples the baseline image to determine if it uses light or dark theme and show whether each changed pixel in the candidate image was added or removed.
This result in a diff image similar to changes in a Pull Request.
Provided a diff pixel buffer that is 3x the width of the baseline pixel buffer, Pbd makes a collage showing the baseline image on the left hand side, the diff in the center and the candidate on the right side. This helps put the diff image in context by showing the baseline and candidate side by side.
Having a single image to get full context can prove easier to integrate in your visual regression report and approval workflow if the baseline, candidate and diff images require to be authenticated to different online storage.
Since it can be difficult to spot subtle or isolated pixel differences in full size screenshots, Pbd can add a minimap, a low resolution, overlay on top of the diff to quickly locate the areas of interest and not miss any visual change.
Pbd returns a simple hash of the pixel differences. This enables to de-duplicate changes than span accross many images.
For instance, if you change the padding in a button that is used accross many visual components of your application, this simple change will be visible in all their screenshots. Thanks to the hash of the changes, your visual regression report can show this change just once and enable you and your team to focus on unique visual changes.
Often times, visual tests that run on your Continuous Integration pipeline will run on different machines and show anti-aliasing differences ( small differences along the visible edges in your images ). Using the threshold of difference per pixel alone would show even the slightest difference. However Pbd returns the cumulatedDiff, the sum of threshold difference of every pixel change, to let you discard anti-aliasing changes which amount to
| Baseline | Candidate | Side by side diff with minimap overlay | 
|---|---|---|
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
|  |  |  | 
Images courtesy of Pixematch and odiff
Pbd exports two types for the diffing Options and Results, and two methods, diff and diffImageDatas which work as a drop in replacement for Pixelmatch or directly with ImageData objects. They are exactly the same in term of functionnality and return value, so use either at your convenience.
The Options type defines the options specifying how to diff pixel buffers and their output.
type Options = {
  threshold?: number;
  cumulatedThreshold?: number;
  enableMinimap?: boolean
};- thresholdspecifies the individual pixel matching threshold between- 0and- 1. Smaller values make the comparison more sensitive. Defaults to- 0.03
- cumulatedThresholdspecifies the cumulated pixel matching threshold. Smaller values make the comparision more sensitive to anti-aliasing differences. Default to- .5
- enableMinimapenables the low resolution overlay. Defaults to- false
The diff method is a drop in replacement for Pixelmatch.
const diff: (
  baseline8: Uint8Array | Uint8ClampedArray,
  candidate8: Uint8Array | Uint8ClampedArray,
  diff8: Uint8Array | Uint8ClampedArray,
  width: number,
  height: number,
  options: Options = defaultOptions
): Result- baseline8,- candidate8and- diff8are- Uint8Arrayor- Uint8ClampedArrayholding the 32bits pixel data for the baseline, candidate and diff images.
- widthand- heightare the width and height of the images.
- optionsdefines how to diff the pixel buffers
The diffImageData method takes ImageData as arguments for a simpler API.
const diffImageDatas: (
  baseline: ImageData,
  candidate: ImageData,
  diff: ImageData,
  options: Options = defaultOptions
): Result- baseline,- candidateand- diffare- ImageDataholding the 32bits pixel data for the baseline, candidate and diff images.
- optionsdefines how to diff the pixel buffers
The diff and diffImageDatas methods mutate the diff pixel buffer they receive as argument and return a Result object.
The Result type defines the properties resulting from diffing two pixel buffers.
type Result = {
  diff: number;
  cumulatedDiff: number;
  hash: number
};- diffa number showing the number of pixels that exceeded the- threshold
- hasha numeric hash representing the pixel change between the two images. This hash allows to de-duplicate changes across multiple images to only show unique changes in your visual regression report and approval workflow.
- cumulatedDiffa number representing the cumulated difference of every pixel change in the two images. This can used to discard changes that only effect subtle differences like anti-aliasing pixels.
These properties are all set to 0 if the two images are within the cumulatedThreshold.
import * as fs from "fs";
import * as fastPng from "fast-png";
import { diff } from "pixel-buffer-diff";
// Read and decode baseline and candidate pngs
const pngBaseline = fastPng.decode(fs.readFileSync("baseline.png"));
const pngCandidate = fastPng.decode(fs.readFileSync("candidate.png"));
// Get necessary properties
const { width, height } = pngBaseline;
// Create diff ImageData: 3x wider to get side by side diff
const diffImageData = {
  width: 3 * width,
  height,
  data: new Uint8ClampedArray(3 * width * height * 4)
};
// Diff images with 1% threshold and minimap overlay to spot isolated changes
const result = diff(
  pngBaseline.data,
  pngCandidate.data,
  diffImageData.data,
  width,
  height,
  {
    threshold: 0.01,
    enableMinimap: true
  });
// Output the result
console.log({...result});
// Save the diff if the cumulated delta is significant
if (result.cumulatedDiff > 0) {
  fs.writeFileSync("diff.png", fastPng.encode(diff as fastPng.IImageData));
}💘 @p01