Skip to Content
New: ssim-native brings SSIM, MS-SSIM and Hitchhiker's to Node, and interpret-native ships diff interpretation on its own. Read more β†’
DocsPixel-by-pixel Image ComparisonπŸ†πŸ† WebAssembly

Pixel-by-pixel Comparison with WebAssembly

@blazediff/core-wasm is the Rust diff core compiled to wasm32 with v128 SIMD, in about 32KB. It runs in browsers, Web Workers, Deno, Bun, Cloudflare Workers, and any other wasm host, with no native dependency and no network call.

Reach for it when you want compiled-code speed on the client or at the edge. In Node, the native binding is faster still and decodes images for you. In an environment where you cannot ship a wasm file at all, use the pure-JS core.

Installation

npm install @blazediff/core-wasm

Decoding images to RGBA

The wasm module takes pre-decoded RGBA and does not bundle an image decoder, so the browser’s own decoder does that half:

async function loadRgba(url: string) { const bitmap = await createImageBitmap(await (await fetch(url)).blob()); const canvas = new OffscreenCanvas(bitmap.width, bitmap.height); const ctx = canvas.getContext("2d")!; ctx.drawImage(bitmap, 0, 0); const { data, width, height } = ctx.getImageData(0, 0, bitmap.width, bitmap.height); return { data: new Uint8Array(data.buffer), width, height }; }

Examples

Fixture A

Image 1

Fixture B

Image 2

Result

import { initBlazediff, diff } from "@blazediff/core-wasm"; await initBlazediff(); // loads the sibling .wasm; call once const a = await loadRgba("3a.png"); const b = await loadRgba("3b.png"); const output = new Uint8Array(a.width * a.height * 4); const diffCount = await diff(a.data, b.data, a.width, a.height, output);

initBlazediff resolves the sibling .wasm by default. Loading it from a CDN, a bundler, or the filesystem instead? It also accepts a URL, a Response, or raw bytes.

What it costs

Against pixelmatch on the same fixtures, decode excluded, M1 Max, Node 22, 25 iterations:

Casepixelmatch@blazediff/core-wasm
4K pair332.26-423.14ms33.18-68.37ms
4K pair, identical19.86-30.53ms17.47-22.17ms

So 5x to 10x on a changed 4K pair, and ~51% faster averaged over the whole fixture set. Identical images are the weak spot: wasm32-unknown-unknown has no libc, so the byte-equality shortcut the other cores use lowers to a scalar memcmp and is skipped here. The block scan reaches the same answer with v128 compares, which is why that row is close rather than behind. Full tables and the history of that fix.

Next

Last updated on