Pixel By Pixel Benchmarks
Comparisons that compare pixels directly (RGBA or YIQ perceptual delta). Image decode is excluded except where the section title explicitly says โimage IO includedโ (native binary, Python bindings).
![]()
JavaScript (@blazediff/core vs pixelmatch) (image IO excluded)
50 iterations (5 warmup)
~61.6% performance improvement on average.
| Benchmark | Pixelmatch | BlazeDiff | Time Saved | % Improvement |
|---|---|---|---|---|
| 4k/1 | 349.90ms | 172.60ms | 177.30ms | 50.7% |
| 4k/1 (identical) | 21.47ms | 3.52ms | 17.95ms | 83.6% |
| 4k/2 | 348.25ms | 182.57ms | 165.68ms | 47.6% |
| 4k/2 (identical) | 22.86ms | 2.60ms | 20.26ms | 88.6% |
| 4k/3 | 432.30ms | 231.08ms | 201.22ms | 46.5% |
| 4k/3 (identical) | 27.66ms | 3.20ms | 24.46ms | 88.4% |
| blazediff/1 | 1.25ms | 0.68ms | 0.57ms | 45.6% |
| blazediff/1 (identical) | 0.50ms | 0.05ms | 0.45ms | 89.9% |
| blazediff/2 | 1.72ms | 1.07ms | 0.64ms | 37.5% |
| blazediff/2 (identical) | 0.40ms | 0.04ms | 0.37ms | 90.7% |
| blazediff/3 | 10.75ms | 11.60ms | -0.85ms | -7.9% |
| blazediff/3 (identical) | 1.90ms | 0.23ms | 1.67ms | 87.7% |
| blazediff/4 | 12.44ms | 7.95ms | 4.49ms | 36.1% |
| blazediff/4 (identical) | 4.92ms | 0.50ms | 4.42ms | 89.9% |
| page/1 | 147.00ms | 94.64ms | 52.36ms | 35.6% |
| page/1 (identical) | 70.36ms | 7.77ms | 62.59ms | 89.0% |
| page/2 | 525.37ms | 253.35ms | 272.02ms | 51.8% |
| page/2 (identical) | 48.83ms | 7.11ms | 41.73ms | 85.4% |
| pixelmatch/1 | 0.60ms | 0.37ms | 0.23ms | 38.9% |
| pixelmatch/1 (identical) | 0.15ms | 0.02ms | 0.13ms | 88.8% |
| pixelmatch/2 | 2.27ms | 1.84ms | 0.43ms | 19.0% |
| pixelmatch/2 (identical) | 0.08ms | 0.01ms | 0.07ms | 90.3% |
| pixelmatch/3 | 0.39ms | 0.27ms | 0.13ms | 32.6% |
| pixelmatch/3 (identical) | 0.15ms | 0.01ms | 0.13ms | 90.3% |
| pixelmatch/4 | 4.20ms | 3.62ms | 0.59ms | 14.0% |
| pixelmatch/4 (identical) | 0.21ms | 0.02ms | 0.19ms | 90.7% |
| pixelmatch/5 | 0.19ms | 0.14ms | 0.05ms | 24.9% |
| pixelmatch/5 (identical) | 0.08ms | 0.01ms | 0.07ms | 90.5% |
| pixelmatch/6 | 0.78ms | 0.81ms | -0.03ms | -3.8% |
| pixelmatch/6 (identical) | 0.08ms | 0.01ms | 0.07ms | 89.6% |
| pixelmatch/7 | 1.24ms | 0.96ms | 0.28ms | 22.5% |
| pixelmatch/7 (identical) | 0.28ms | 0.03ms | 0.26ms | 90.9% |
| same/1 | 2.05ms | 0.22ms | 1.83ms | 89.5% |
| same/1 (identical) | 2.03ms | 0.22ms | 1.81ms | 89.0% |
Benchmarks run on MacBook Pro M1 Max, Node.js 22
JavaScript with output buffer (@blazediff/core vs pixelmatch) (image IO excluded)
50 iterations (5 warmup)
~30.9% performance improvement on average.
| Benchmark | Pixelmatch | BlazeDiff | Time Saved | % Improvement |
|---|---|---|---|---|
| 4k/1 | 394.73ms | 179.14ms | 215.59ms | 54.6% |
| 4k/1 (identical) | 75.70ms | 52.08ms | 23.63ms | 31.2% |
| 4k/2 | 401.19ms | 194.79ms | 206.40ms | 51.4% |
| 4k/2 (identical) | 85.04ms | 54.78ms | 30.26ms | 35.6% |
| 4k/3 | 504.06ms | 242.61ms | 261.45ms | 51.9% |
| 4k/3 (identical) | 99.18ms | 65.62ms | 33.56ms | 33.8% |
| blazediff/1 | 2.66ms | 1.95ms | 0.71ms | 26.6% |
| blazediff/1 (identical) | 2.00ms | 1.30ms | 0.70ms | 35.1% |
| blazediff/2 | 2.91ms | 2.13ms | 0.77ms | 26.6% |
| blazediff/2 (identical) | 1.43ms | 0.96ms | 0.47ms | 33.1% |
| blazediff/3 | 17.72ms | 16.55ms | 1.17ms | 6.6% |
| blazediff/3 (identical) | 6.81ms | 4.82ms | 1.99ms | 29.3% |
| blazediff/4 | 24.98ms | 19.15ms | 5.83ms | 23.3% |
| blazediff/4 (identical) | 15.53ms | 10.30ms | 5.23ms | 33.7% |
| page/1 | 347.32ms | 270.86ms | 76.46ms | 22.0% |
| page/1 (identical) | 241.59ms | 161.64ms | 79.94ms | 33.1% |
| page/2 | 650.76ms | 367.04ms | 283.72ms | 43.6% |
| page/2 (identical) | 175.32ms | 118.94ms | 56.38ms | 32.2% |
| pixelmatch/1 | 1.04ms | 0.84ms | 0.20ms | 18.8% |
| pixelmatch/1 (identical) | 0.54ms | 0.37ms | 0.18ms | 32.7% |
| pixelmatch/2 | 2.46ms | 1.99ms | 0.47ms | 19.2% |
| pixelmatch/2 (identical) | 0.28ms | 0.18ms | 0.10ms | 36.3% |
| pixelmatch/3 | 0.86ms | 0.64ms | 0.22ms | 25.3% |
| pixelmatch/3 (identical) | 0.52ms | 0.36ms | 0.16ms | 31.3% |
| pixelmatch/4 | 4.85ms | 3.92ms | 0.93ms | 19.2% |
| pixelmatch/4 (identical) | 0.74ms | 0.49ms | 0.25ms | 33.7% |
| pixelmatch/5 | 0.41ms | 0.32ms | 0.10ms | 23.2% |
| pixelmatch/5 (identical) | 0.26ms | 0.18ms | 0.08ms | 31.8% |
| pixelmatch/6 | 1.17ms | 0.89ms | 0.28ms | 23.8% |
| pixelmatch/6 (identical) | 0.26ms | 0.18ms | 0.08ms | 31.5% |
| pixelmatch/7 | 2.10ms | 1.66ms | 0.44ms | 20.8% |
| pixelmatch/7 (identical) | 1.01ms | 0.68ms | 0.33ms | 32.6% |
| same/1 | 7.09ms | 4.76ms | 2.32ms | 32.8% |
| same/1 (identical) | 7.15ms | 4.73ms | 2.42ms | 33.8% |
WebAssembly (@blazediff/core-wasm vs pixelmatch) (image IO excluded)
25 iterations (5 warmup)
~58% performance improvement on average.
The WebAssembly build of BlazeDiff uses the same Rust algorithm as the native binary, compiled to wasm32 with v128 SIMD (+simd128). Counts agree with pixelmatch to within ~0.05% across the fixture set (e.g. pixelmatch/1: identical 106 vs 106; blazediff/3: 22 869 vs 22 883 out of 1 630 784 pixels; 4k/1: 69 932 vs 69 912 out of 17 920 000): both use a YIQ-style perceptual delta, so they classify the same pixels modulo a handful of edge cases.
| Benchmark | Pixelmatch | BlazeDiff (core-wasm) | Time Saved | % Improvement |
|---|---|---|---|---|
| 4k/1 | 287.72ms | 51.75ms | 235.97ms | 82.0% |
| 4k/1 (identical) | 24.82ms | 14.59ms | 10.23ms | 41.2% |
| 4k/2 | 299.62ms | 74.35ms | 225.27ms | 75.2% |
| 4k/2 (identical) | 27.83ms | 18.78ms | 9.05ms | 32.5% |
| 4k/3 | 366.81ms | 69.90ms | 296.91ms | 80.9% |
| 4k/3 (identical) | 33.24ms | 21.60ms | 11.65ms | 35.0% |
| blazediff/1 | 2.54ms | 0.35ms | 2.19ms | 86.4% |
| blazediff/1 (identical) | 0.60ms | 0.27ms | 0.33ms | 55.6% |
| blazediff/2 | 2.67ms | 0.47ms | 2.20ms | 82.4% |
| blazediff/2 (identical) | 0.48ms | 0.22ms | 0.26ms | 54.6% |
| blazediff/3 | 14.60ms | 5.52ms | 9.09ms | 62.2% |
| blazediff/3 (identical) | 2.23ms | 1.22ms | 1.01ms | 45.1% |
| page/1 | 317.16ms | 63.97ms | 253.19ms | 79.8% |
| page/1 (identical) | 81.91ms | 59.47ms | 22.44ms | 27.4% |
| page/2 | 443.83ms | 109.74ms | 334.10ms | 75.3% |
| page/2 (identical) | 58.12ms | 38.62ms | 19.51ms | 33.6% |
| pixelmatch/1 | 0.87ms | 0.13ms | 0.74ms | 84.6% |
| pixelmatch/1 (identical) | 0.18ms | 0.08ms | 0.10ms | 55.8% |
| pixelmatch/2 | 2.10ms | 1.28ms | 0.81ms | 38.7% |
| pixelmatch/2 (identical) | 0.09ms | 0.04ms | 0.05ms | 56.6% |
| pixelmatch/3 | 0.74ms | 0.12ms | 0.62ms | 84.0% |
| pixelmatch/3 (identical) | 0.18ms | 0.08ms | 0.10ms | 57.3% |
| pixelmatch/4 | 4.23ms | 2.73ms | 1.50ms | 35.4% |
| pixelmatch/4 (identical) | 0.24ms | 0.12ms | 0.13ms | 51.5% |
| pixelmatch/5 | 0.37ms | 0.06ms | 0.31ms | 84.2% |
| pixelmatch/5 (identical) | 0.09ms | 0.04ms | 0.05ms | 56.5% |
| pixelmatch/6 | 0.90ms | 0.52ms | 0.38ms | 41.9% |
| pixelmatch/6 (identical) | 0.09ms | 0.05ms | 0.05ms | 50.1% |
| pixelmatch/7 | 1.86ms | 0.58ms | 1.28ms | 68.8% |
| pixelmatch/7 (identical) | 0.35ms | 0.16ms | 0.19ms | 54.2% |
| same/1 | 2.50ms | 1.19ms | 1.31ms | 52.4% |
| same/1 (identical) | 2.48ms | 1.43ms | 1.05ms | 42.4% |
Benchmarks run on MacBook Pro M1 Max, Node.js 22
JavaScript Native Binary (@blazediff/core-native vs odiff) (image IO included)
25 runs (5 warmup)
3-4x faster than odiff on 4K images. ~43.5% performance improvement on average.
The native Rust binary with SIMD optimization is the fastest single-threaded image diff in the world.
| Benchmark | ODiff | BlazeDiff | BlazeDiff Next | BlazeDiff Saved | BlazeDiff % | BlazeDiff Next Saved | BlazeDiff Next % |
|---|---|---|---|---|---|---|---|
| 4k/1 | 1222.71ms | 282.45ms | 218.92ms | 940.26ms | 76.9% | 1003.79ms | 82.1% |
| 4k/1 (identical) | 276.93ms | 183.25ms | 142.57ms | 93.68ms | 33.8% | 134.37ms | 48.5% |
| 4k/2 | 1547.67ms | 344.91ms | 268.42ms | 1202.75ms | 77.7% | 1279.25ms | 82.7% |
| 4k/2 (identical) | 358.87ms | 223.04ms | 165.06ms | 135.84ms | 37.9% | 193.81ms | 54.0% |
| 4k/3 | 1762.35ms | 356.26ms | 291.91ms | 1406.10ms | 79.8% | 1470.45ms | 83.4% |
| 4k/3 (identical) | 381.14ms | 228.13ms | 169.34ms | 153.01ms | 40.1% | 211.81ms | 55.6% |
| blazediff/1 | 3.96ms | 2.16ms | 1.79ms | 1.80ms | 45.5% | 2.17ms | 54.9% |
| blazediff/1 (identical) | 1.58ms | 0.93ms | 0.81ms | 0.64ms | 40.7% | 0.76ms | 48.3% |
| blazediff/2 | 4.01ms | 2.37ms | 2.56ms | 1.64ms | 40.9% | 1.44ms | 36.0% |
| blazediff/2 (identical) | 1.44ms | 1.00ms | 0.78ms | 0.44ms | 30.4% | 0.66ms | 46.1% |
| blazediff/3 | 46.83ms | 23.01ms | 18.81ms | 23.81ms | 50.9% | 28.02ms | 59.8% |
| blazediff/3 (identical) | 19.25ms | 14.64ms | 11.42ms | 4.61ms | 24.0% | 7.83ms | 40.7% |
| blazediff/4 | 22.58ms | 19.29ms | 13.24ms | 3.29ms | 14.6% | 9.34ms | 41.4% |
| blazediff/4 (identical) | 9.13ms | 4.57ms | 3.73ms | 4.57ms | 50.0% | 5.41ms | 59.2% |
| page/1 | 1034.08ms | 467.82ms | 339.61ms | 566.26ms | 54.8% | 694.47ms | 67.2% |
| page/1 (identical) | 566.00ms | 244.78ms | 165.71ms | 321.22ms | 56.8% | 400.29ms | 70.7% |
| page/2 | 601.68ms | 264.74ms | 233.56ms | 336.95ms | 56.0% | 368.12ms | 61.2% |
| page/2 (identical) | 106.28ms | 41.46ms | 33.51ms | 64.82ms | 61.0% | 72.77ms | 68.5% |
| pixelmatch/1 | 3.48ms | 1.57ms | 1.22ms | 1.91ms | 54.9% | 2.27ms | 65.1% |
| pixelmatch/1 (identical) | 1.77ms | 1.15ms | 0.85ms | 0.62ms | 35.2% | 0.92ms | 52.0% |
| pixelmatch/2 | 3.50ms | 2.15ms | 1.41ms | 1.35ms | 38.5% | 2.09ms | 59.7% |
| pixelmatch/2 (identical) | 0.50ms | 0.57ms | 0.22ms | -0.06ms | -12.5% | 0.28ms | 56.6% |
| pixelmatch/3 | 2.53ms | 1.25ms | 0.91ms | 1.28ms | 50.6% | 1.62ms | 64.0% |
| pixelmatch/3 (identical) | 1.73ms | 0.86ms | 0.62ms | 0.87ms | 50.1% | 1.11ms | 64.4% |
| pixelmatch/4 | 9.74ms | 4.75ms | 4.29ms | 4.99ms | 51.2% | 5.45ms | 55.9% |
| pixelmatch/4 (identical) | 2.85ms | 2.11ms | 1.24ms | 0.74ms | 26.0% | 1.61ms | 56.3% |
| pixelmatch/5 | 0.91ms | 0.61ms | 0.92ms | 0.30ms | 32.6% | -0.02ms | -2.1% |
| pixelmatch/5 (identical) | 1.24ms | 0.68ms | 0.40ms | 0.56ms | 44.8% | 0.84ms | 68.1% |
| pixelmatch/6 | 5.62ms | 1.75ms | 1.55ms | 3.87ms | 68.9% | 4.07ms | 72.5% |
| pixelmatch/6 (identical) | 1.09ms | 0.85ms | 0.47ms | 0.24ms | 22.0% | 0.61ms | 56.5% |
| pixelmatch/7 | 3.44ms | 1.97ms | 1.39ms | 1.47ms | 42.7% | 2.04ms | 59.5% |
| pixelmatch/7 (identical) | 0.88ms | 0.59ms | 0.32ms | 0.29ms | 33.0% | 0.56ms | 63.3% |
| same/1 | 5.56ms | 3.43ms | 2.67ms | 2.13ms | 38.3% | 2.89ms | 51.9% |
| same/1 (identical) | 5.09ms | 3.60ms | 2.52ms | 1.49ms | 29.3% | 2.57ms | 50.5% |
Benchmarks run on MacBook Pro M1 Max using hyperfine
Python Bindings (blazediff PyPI via PyO3) (image IO included)
The PyO3-backed blazediff PyPI package wraps the same Rust core as the native binary; published as platform-tagged wheels (manylinux / macOS / Windows). Comparisons below use the path-based compare() API, so PNG decode is part of the timed region (directly comparable to the Native Binary section above).
vs pixelmatch (pypi)
25 iterations (5 warmup) for blazediff; 10 iterations (5 warmup) for pixelmatch - pure-Python pixelmatch runs many seconds per call on 4k/page fixtures.
~83% performance improvement on average.
| Benchmark | pixelmatch (pypi) | BlazeDiff | Time Saved | % Improvement |
|---|---|---|---|---|
| 4k/1 | 19.54s | 215.03ms | 19.33s | 98.9% |
| 4k/1 (identical) | 610.87ms | 188.91ms | 421.96ms | 69.1% |
| 4k/2 | 23.28s | 259.39ms | 23.02s | 98.9% |
| 4k/2 (identical) | 713.84ms | 234.29ms | 479.55ms | 67.2% |
| 4k/3 | 26.32s | 269.23ms | 26.05s | 99.0% |
| 4k/3 (identical) | 776.98ms | 239.67ms | 537.31ms | 69.2% |
| blazediff/1 | 262.81ms | 0.85ms | 261.97ms | 99.7% |
| blazediff/1 (identical) | 4.44ms | 0.84ms | 3.60ms | 81.1% |
| blazediff/2 | 273.60ms | 1.12ms | 272.48ms | 99.6% |
| blazediff/2 (identical) | 3.90ms | 0.94ms | 2.96ms | 75.9% |
| blazediff/3 | 1.51s | 14.92ms | 1.50s | 99.0% |
| blazediff/3 (identical) | 46.03ms | 14.73ms | 31.30ms | 68.0% |
| page/1 | 36.52s | 264.83ms | 36.26s | 99.3% |
| page/1 (identical) | 1.09s | 264.56ms | 826.66ms | 75.8% |
| page/2 | 41.90s | 99.35ms | 41.80s | 99.8% |
| page/2 (identical) | 171.92ms | 56.01ms | 115.90ms | 67.4% |
| pixelmatch/1 | 93.52ms | 1.06ms | 92.46ms | 98.9% |
| pixelmatch/1 (identical) | 2.85ms | 1.24ms | 1.61ms | 56.6% |
| pixelmatch/2 | 177.75ms | 0.74ms | 177.01ms | 99.6% |
| pixelmatch/2 (identical) | 0.90ms | 0.45ms | 0.44ms | 49.4% |
| pixelmatch/3 | 87.52ms | 0.83ms | 86.69ms | 99.1% |
| pixelmatch/3 (identical) | 2.24ms | 0.84ms | 1.40ms | 62.5% |
| pixelmatch/4 | 365.96ms | 2.71ms | 363.25ms | 99.3% |
| pixelmatch/4 (identical) | 6.05ms | 2.12ms | 3.93ms | 64.9% |
| pixelmatch/5 | 52.21ms | 0.51ms | 51.70ms | 99.0% |
| pixelmatch/5 (identical) | 1.21ms | 0.54ms | 0.67ms | 55.0% |
| pixelmatch/6 | 99.65ms | 1.17ms | 98.48ms | 98.8% |
| pixelmatch/6 (identical) | 1.61ms | 0.78ms | 0.83ms | 51.7% |
| pixelmatch/7 | 193.54ms | 0.66ms | 192.88ms | 99.7% |
| pixelmatch/7 (identical) | 1.86ms | 0.64ms | 1.22ms | 65.7% |
| same/1 | 25.69ms | 3.32ms | 22.37ms | 87.1% |
| same/1 (identical) | 23.47ms | 3.28ms | 20.18ms | 86.0% |
vs opencv-python (cv2.absdiff)
25 iterations (5 warmup)
~69% performance improvement on average.
OpenCVโs cv2.absdiff is a grayscale absolute-difference baseline (the snippet from the OpenCV cookbook); blazediff additionally computes a YIQ perceptual delta with anti-aliasing detection, yet still wins on every fixture.
| Benchmark | OpenCV absdiff | BlazeDiff | Time Saved | % Improvement |
|---|---|---|---|---|
| 4k/1 | 538.87ms | 215.03ms | 323.84ms | 60.1% |
| 4k/1 (identical) | 573.01ms | 188.91ms | 384.10ms | 67.0% |
| 4k/2 | 752.53ms | 259.39ms | 493.14ms | 65.5% |
| 4k/2 (identical) | 700.45ms | 234.29ms | 466.16ms | 66.6% |
| 4k/3 | 803.75ms | 269.23ms | 534.52ms | 66.5% |
| 4k/3 (identical) | 784.05ms | 239.67ms | 544.38ms | 69.4% |
| blazediff/1 | 4.53ms | 0.85ms | 3.68ms | 81.3% |
| blazediff/1 (identical) | 4.66ms | 0.84ms | 3.82ms | 82.0% |
| blazediff/2 | 4.05ms | 1.12ms | 2.93ms | 72.3% |
| blazediff/2 (identical) | 4.06ms | 0.94ms | 3.12ms | 76.8% |
| blazediff/3 | 45.08ms | 14.92ms | 30.16ms | 66.9% |
| blazediff/3 (identical) | 44.88ms | 14.73ms | 30.15ms | 67.2% |
| page/1 | 1.06s | 264.83ms | 799.20ms | 75.1% |
| page/1 (identical) | 1.06s | 264.56ms | 791.06ms | 74.9% |
| page/2 | 290.31ms | 99.35ms | 190.96ms | 65.8% |
| page/2 (identical) | 286.79ms | 56.01ms | 230.77ms | 80.5% |
| pixelmatch/1 | 3.62ms | 1.06ms | 2.55ms | 70.6% |
| pixelmatch/1 (identical) | 3.75ms | 1.24ms | 2.51ms | 67.0% |
| pixelmatch/2 | 1.11ms | 0.74ms | 0.37ms | 33.4% |
| pixelmatch/2 (identical) | 1.18ms | 0.45ms | 0.73ms | 61.6% |
| pixelmatch/3 | 3.29ms | 0.83ms | 2.46ms | 74.7% |
| pixelmatch/3 (identical) | 3.38ms | 0.84ms | 2.54ms | 75.1% |
| pixelmatch/4 | 6.35ms | 2.71ms | 3.63ms | 57.2% |
| pixelmatch/4 (identical) | 5.71ms | 2.12ms | 3.59ms | 62.8% |
| pixelmatch/5 | 1.72ms | 0.51ms | 1.21ms | 70.5% |
| pixelmatch/5 (identical) | 1.85ms | 0.54ms | 1.31ms | 70.6% |
| pixelmatch/6 | 2.57ms | 1.17ms | 1.40ms | 54.4% |
| pixelmatch/6 (identical) | 2.33ms | 0.78ms | 1.55ms | 66.6% |
| pixelmatch/7 | 1.98ms | 0.66ms | 1.32ms | 66.5% |
| pixelmatch/7 (identical) | 1.91ms | 0.64ms | 1.27ms | 66.6% |
| same/1 | 18.22ms | 3.32ms | 14.90ms | 81.8% |
| same/1 (identical) | 18.17ms | 3.28ms | 14.89ms | 81.9% |
Benchmarks run on MacBook Pro M1 Max, Python 3.11