Skip to Content
New: blazediff-png - a from-scratch Rust PNG codec, byte-exact to libspng and faster on every fixture. Read more β†’
APIs@blazediff/core-native

@blazediff/core-native

The fastest single-threaded image diff in the world. Native Rust implementation with SIMD optimization, 3-4x faster and 3x smaller than odiffΒ .

View Detailed BenchmarksΒ 

This package was previously published as @blazediff/bin, which is now deprecated. Please use @blazediff/core-native instead.

Installation

npm install @blazediff/core-native

Also available as a Rust crate: cargo install blazediff

Pre-built binaries are included for all major platforms - no compilation required:

  • macOS ARM64 (Apple Silicon) & x64 (Intel)
  • Linux ARM64 & x64
  • Windows ARM64 & x64

Features

  • PNG, JPEG & QOI support - auto-detected by file extension or encoded bytes
  • 3-4x faster than odiff, 3x smaller binaries (~700KB-900KB vs ~2-3MB)
  • SIMD-accelerated - NEON on ARM, SSE4.1 on x86
  • Block-based optimization - skips unchanged regions
  • Interpret mode - region detection, classification, severity scoring, human-readable summaries

Vendored Libraries

  • libspngΒ  - Fast PNG decoding/encoding with SIMD
  • libjpeg-turboΒ  - High-performance JPEG codec with SIMD
  • qoiΒ  - QOI (Quite OK Image) format for fast lossless compression

API Reference

compare(base, comparison, diffOutput, options?)

Compares two images from file paths or encoded Buffer/Uint8Array inputs and optionally generates a diff image. Both inputs must use the same input type. Format is auto-detected from the file extension or encoded bytes.

Parameters

ParameterTypeDescription
basestring | Uint8ArrayBase/expected image path or encoded bytes
comparisonstring | Uint8ArrayComparison/actual image path or encoded bytes
diffOutputstringPath where the diff image will be saved
optionsBlazeDiffOptionsComparison options (optional)
Options
OptionTypeDefaultDescription
thresholdnumber0.1Color difference threshold (0.0-1.0). Lower = more strict
antialiasingbooleanfalseEnable anti-aliasing detection
diffMaskbooleanfalseOutput only differences with transparent background
diffColorAlt[number, number, number]diff colorAlternative RGB color for darkening differences
interpretbooleanfalseGenerate the diff image and structured interpretation in one pass

Return Types

type BlazeDiffResult = | { match: true; interpretation?: InterpretResult } | { match: false; reason: "layout-diff" } | { match: false; reason: "pixel-diff"; diffCount: number; diffPercentage: number; interpretation?: InterpretResult } | { match: false; reason: "file-not-exists"; file: string };

When interpret: true, the requested diff output is written and the result includes an interpretation field from the same diff pass.

Encoded inputs are passed by reference across the N-API boundary. Rust borrows the existing JavaScript backing memory for the synchronous call. Image decoding still allocates native RGBA pixel buffers.

Threshold Guidelines:

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

Usage

Programmatic API

import { compare } from '@blazediff/core-native'; const result = await compare('expected.png', 'actual.png', 'diff.png', { threshold: 0.1, antialiasing: true, }); if (result.match) { console.log('Images are identical!'); } else if (result.reason === 'pixel-diff') { console.log(`${result.diffCount} pixels differ (${result.diffPercentage.toFixed(2)}%)`); } else if (result.reason === 'layout-diff') { console.log('Images have different dimensions'); }

Encoded Buffer Input

import { readFile } from "node:fs/promises"; import { compare } from "@blazediff/core-native"; const [expected, actual] = await Promise.all([ readFile("expected.png"), readFile("actual.png"), ]); // Reuse these same Buffer objects across comparisons without a JS-to-Rust copy. const result = await compare(expected, actual, "diff.png");

CLI Usage

# Compare two PNG images npx blazediff expected.png actual.png diff.png # Compare two JPEG images npx blazediff expected.jpg actual.jpg diff.jpg # Compare two QOI images npx blazediff expected.qoi actual.qoi diff.qoi # Mixed formats (PNG input, QOI output - recommended for smallest diff files) npx blazediff expected.png actual.png diff.qoi # With options npx blazediff expected.png actual.png diff.png --threshold 0.05 --antialiasing # With higher PNG compression (smaller output file, slower) npx blazediff expected.png actual.png diff.png -c 6 # With JPEG quality setting npx blazediff expected.jpg actual.jpg diff.jpg -q 85 # Output as text format npx blazediff expected.png actual.png diff.png --output-format text

CLI Options

blazediff [OPTIONS] <IMAGE1> <IMAGE2> [OUTPUT] Arguments: <IMAGE1> First image path (PNG, JPEG, or QOI) <IMAGE2> Second image path (PNG, JPEG, or QOI) [OUTPUT] Output diff image path (optional, format detected from extension) Options: -t, --threshold <THRESHOLD> Color difference threshold (0.0-1.0) [default: 0.1] -a, --antialiasing Enable anti-aliasing detection --diff-mask Output only differences (transparent background) --diff-color-alt <R,G,B> Alternative RGB color for darkening differences -c, --compression <LEVEL> PNG compression level (0-9, 0=fastest, 9=smallest) [default: 0] -q, --quality <QUALITY> JPEG quality (1-100) [default: 90] --interpret Run structured interpretation after diff --output-format <FORMAT> Output format (json or text) [default: json] -h, --help Print help -V, --version Print version

Supported Formats

FormatExtensionsNotes
PNG.pngLossless, supports transparency
JPEG.jpg, .jpegLossy, smaller file sizes
QOI.qoiFast lossless, ideal for diff outputs (12x smaller than uncompressed PNG)

Input images can be mixed formats (e.g., compare PNG to JPEG). Output format is determined by the output file extension.

Use QOI for diff outputs: QOI excels at encoding diff images with large uniform areas, producing files 12x smaller than PNG (level 0) while being faster to encode.

Exit Codes

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

Interpret

Structured diff analysis for matching path or encoded byte inputs. Returns classified change regions with human-readable summaries.

Pipeline: change mask β†’ morph close β†’ connected components β†’ per-region evidence extraction β†’ classify β†’ describe

Evidence per region:

  • Dual-image gradient comparison (edges in both images + spatial correlation)
  • YIQ color delta with uniformity analysis (mean, max, stddev)
  • Background distance (changed pixels vs local unchanged pixels)
  • Shape statistics (fill ratio, border density, occupancy)

Six-label decision tree:

TypeSignal
RenderingNoiseTiny (≀25px) or sparse + low color delta
AdditionBlends with background in img1, distinct in img2
DeletionDistinct in img1, blends with background in img2
ColorChangeEdge structure preserved across both images + uniform color shift
ContentChangeFallback - structural change
ShiftPost-hoc: matched Addition+Deletion pair with similar luminance
import { compare, interpret } from '@blazediff/core-native'; const result = await interpret('expected.png', 'actual.png'); console.log(result.summary); // "Moderate visual change detected (1.87% of image, 10 regions). // Content changed: 4 regions (bottom, center). // Content added: 3 regions (right, bottom, bottom-left)." // Via compare() const diff = await compare('expected.png', 'actual.png', 'diff.png', { interpret: true, });
npx blazediff expected.png actual.png --interpret npx blazediff expected.png actual.png report.html --output-format html

Severity: Low (<1%), Medium (1-10%), High (>10%). See Interpret example β†’ for interactive demo.

Accuracy: Measured against hand-labeled datasets β€” classifier-only macro F1 is 0.998 on clean add/delete edits (addition_deletion) and 0.440 on inpaint edits that mix recolor and texture replacement (inpaintcoco). Full breakdown in BENCHMARKS.mdΒ .

Performance

Benchmarked on Apple M1 Max with 5600Γ—3200 4K images (25 runs, 5 warmup, image IO included):

ToolTimeComparison
blazediff~359ms-
odiff~1221ms3.4x slower

Binary sizes (stripped, LTO optimized):

Platformblazediffodiff
macOS ARM64702 KB2.2 MB
Linux x64869 KB2.9 MB
Windows x64915 KB3.0 MB

Why so fast? BlazeDiff uses a two-pass block-based algorithm with SIMD acceleration. The cold pass quickly identifies unchanged 8x8 blocks using 32-bit integer comparison, then the hot pass only processes changed regions with YIQ perceptual color difference.

Last updated on