Skip to Content
New: codec-png-native and codec-png-wasm bring the blazediff-png codec to Node and WebAssembly, faster than spng and jSquash. Read more β†’
APIsblazediff

blazediff (Rust)

The Rust crate that powers the entire BlazeDiff stack. 3-4x faster than odiffΒ , 8x faster than pixelmatch on 4K images. Block-based diff algorithm with SIMD-accelerated YIQ color comparison; the same engine drives @blazediff/core-native (Node.js) and blazediff (Python).

View Detailed BenchmarksΒ 

Installation

As a CLI

cargo install blazediff

As a library

# Cargo.toml [dependencies] blazediff = "*"

Pre-built binaries (via cargo install) and crate sources are available on crates.ioΒ .

Features

  • PNG, JPEG & QOI support with vendored libspng, libjpeg-turbo, and qoi-rust (no system dependencies)
  • SIMD-accelerated - NEON on ARM, SSE4.1 on x86
  • Block-based optimization - skips unchanged 8x8 blocks via 32-bit integer compare before perceptual diff
  • Interpret mode - region detection, classification, severity scoring, human-readable summaries
  • Cargo features - opt into napi (Node.js bindings) or python (PyO3 bindings) when building from source

CLI Usage

# Basic diff blazediff image1.png image2.png -o diff.png # Custom threshold (0.0 - 1.0) blazediff image1.png image2.png -o diff.png -t 0.05 # JPEG comparison blazediff a.jpg b.jpg -o diff.jpg -q 85 # QOI diff output (12x smaller than PNG, faster encode) blazediff a.png b.png -o diff.qoi # pixelmatch 8 metric and the busiest 16Γ—16 window, as JSON blazediff a.png b.png --metric oklab --window 16 --json # Structured interpretation (separate crate and binary) blazediff-interpret a.png b.png --json

CLI Options

Usage: blazediff [OPTIONS] <A> <B> Options: -o, --output <OUTPUT> Write the diff image here (only when the images differ) -t, --threshold <THRESHOLD> Color difference threshold (0.0-1.0) [default: 0.1] --antialiasing Detect anti-aliased pixels and leave them out of the count (default) --no-antialiasing Count anti-aliased pixels as differences --metric <METRIC> Color difference metric: yiq or oklab (pixelmatch 8) [default: yiq] --mask <MASK> Image the size of the inputs; its non-black pixels are skipped --window <WINDOW> Also report the NΓ—N window with the most diff pixels -a, --alpha <ALPHA> Opacity of the faded background (0.0-1.0) [default: 0.1] --diff-color <R,G,B> RGB color for differing pixels --diff-color-alt <R,G,B> RGB color for pixels that get darker in the second image --aa-color <R,G,B> RGB color for anti-aliased pixels --background <BACKGROUND> faded or transparent [default: faded] -c, --compression <COMPRESSION> PNG compression level (0-9) [default: 0] -q, --quality <QUALITY> JPEG quality (1-100) [default: 90] --json Print the result as JSON -h, --help Print help -V, --version Print version

Exit Codes

  • 0 - Images are identical
  • 1 - Images differ (includes a dimension mismatch)
  • 2 - Error (file not found, invalid format, etc.)

Library Usage

The crate exposes diff() plus codec helpers for PNG, JPEG, and QOI. Decode once, diff in memory, encode if you want a diff image.

use blazediff::{diff, load_pngs, save_png, DiffOptions, DiffResult, Image}; let (img1, img2) = load_pngs("expected.png", "actual.png")?; let options = DiffOptions { threshold: 0.1, antialiasing: false, // count anti-aliased pixels too ..Default::default() }; let mut output = Image::new(img1.width, img1.height); match diff(&img1, &img2, Some(&mut output), &options)? { DiffResult::Compared { count, percentage, identical, .. } => { println!("{count} pixels differ ({percentage:.2}%)"); if !identical { save_png(&output, "diff.png")?; } } DiffResult::DimensionMismatch { a, b } => println!("Sizes differ: {a:?} vs {b:?}"), }

Types

pub struct DiffOptions { pub threshold: f64, // 0.0-1.0, default 0.1 pub antialiasing: bool, // detect and exclude anti-aliased pixels, default true pub metric: Metric, // Yiq (default) or Oklab (pixelmatch 8) pub mask: Option<Vec<u8>>, // one byte per pixel; non-zero is skipped pub window: Option<u32>, // also report the busiest NΓ—N window pub render: RenderOptions, } pub struct RenderOptions { pub alpha: f64, // background opacity, default 0.1 pub diff_color: [u8; 3], // default red [255, 0, 0] pub diff_color_alt: Option<[u8; 3]>, // pixels that get darker in the second image pub aa_color: [u8; 3], // default yellow [255, 255, 0] pub background: Background, // Faded (default) or Transparent } pub enum DiffResult { Compared { count: u32, percentage: f64, identical: bool, window: Option<WindowHit> }, DimensionMismatch { a: Size, b: Size }, } pub struct WindowHit { pub x: u32, pub y: u32, pub count: u32 } // top-left corner and count

diff returns Err only for buffers that do not match their size and masks of the wrong length. PNG compression is an argument of save_png_with_compression, not a diff option.

Codec helpers

FunctionFormat
load_png / load_pngs / save_png / encode_pngPNG
load_jpeg / load_jpegs / save_jpegJPEG
load_qoi / load_qois / save_qoiQOI

All decoders return Image (RGBA8, tightly packed). Inputs can be mixed formats - decode each side with the matching helper, then call diff().

Threshold guidelines:

  • 0.0 - Exact match only
  • 0.05 - Strict comparison
  • 0.1 - Default balanced comparison
  • 0.2 - Lenient comparison

Cargo Features

FeaturePurpose
(default)Pure Rust library + CLI
napiN-API bindings used by @blazediff/core-native on NPM
pythonPyO3 bindings used by the blazediff package on PyPI

The napi and python features are mutually exclusive build modes for distributing prebuilt artifacts; you don’t need them when consuming the crate as a Rust dependency.

Interpret

Structured diff analysis. Takes two images, returns classified change regions with human-readable summaries. It is not part of this crate: it lives in blazediff-interpret, which consumes what diff returns and ships its own blazediff-interpret binary.

Pipeline: change mask -> morph close -> connected components -> noise census + fragment merge -> per-region evidence extraction -> classify -> shift pairing -> describe

Six-label rule cascade:

TypeSignal
RenderingNoiseTiny (<=25px) or sparse + low color delta, without an add/delete signature
AdditionBlends with background in img1, distinct in img2
DeletionDistinct in img1, blends with background in img2
ColorChangeLuminance structure preserved under a color shift, or a coherent chroma move (hue rotation, smooth delta field) over regenerated texture
ContentChangeFallback - structure replaced, chroma scattered
ShiftPost-hoc: region pair whose before-crop and after-crop hold the same content, matched by patch correlation
blazediff-interpret a.png b.png blazediff-interpret a.png b.png diff.png --json

Severity tiers: Low (<1%), Medium (1-10%), High (>10%). See Interpret example β†’ for the interactive demo, and INTERPRET.mdΒ  for the full algorithm spec.

Vendored libraries

No system C dependencies; the crate builds standalone with a recent Rust toolchain.

Last updated on