@blazediff/core-wasm

中文文档
7.0.0发布于 2026/11/05MITTypeScriptES Module构建来源已验证近 30 天下载 1.3万

WebAssembly build of blazediff for browsers, edge runtimes, and any wasm host. Same algorithm as @blazediff/core-native.

原始使用文档
文档目录16 节
  1. Installation
  2. Loading the wasm module
  3. Browser (default fetch)
  4. Universal CDN URL (recommended for Node, Workers, Deno, Bun)
  5. Bundlers (Vite, Webpack 5+, esbuild, Rollup with plugin)
  6. Node from the local filesystem
  7. API
  8. initBlazediff(input?)
  9. diff(a, b, options?)
  10. Usage
  11. Browser
  12. Node
  13. Performance
  14. Algorithm
  15. Picking the right package
  16. References

@blazediff/core-wasm

npm bundle size NPM Downloads Crates.io

WebAssembly build of the BlazeDiff Rust algorithm for browsers, edge runtimes, and any wasm host. Same two-pass block algorithm as @blazediff/core-native, compiled to wasm32 with v128 SIMD (+simd128). ~51% faster than pixelmatch on the same RGBA buffers (up to ~90% on 4K); diff counts agree with pixelmatch to within ~0.05%.

Features:

  • Same Rust algorithm as @blazediff/core-native (YIQ perceptual delta + block-based cold/hot pass)
  • wasm32 v128 SIMD: 4-lane vectorized cold and hot loops; up to ~16x faster than pixelmatch on 4K
  • Same Image, DiffOptions and DiffResult as @blazediff/core. Caller decodes images; no PNG/JPEG codecs bundled
  • ~32 KB optimized wasm + ~10 KB JS glue. No native binaries, no postinstall, no platform packages
  • Runs anywhere wasm runs: browsers, Node 18+, Cloudflare Workers, Deno, Bun

Installation

npm install @blazediff/core-wasm

Loading the wasm module

Pick the recipe that matches your runtime. All four are equivalent; the wasm itself is the same.

Browser (default fetch)

The wasm-bindgen --target web glue fetches the sibling .wasm via import.meta.url automatically:

import { initBlazediff } from '@blazediff/core-wasm';
await initBlazediff();

jsDelivr serves the published .wasm over HTTPS, so any fetch()-capable runtime can load it. One network round-trip on cold start, cached by the runtime after that:

import { initBlazediff } from '@blazediff/core-wasm';

await initBlazediff(
  new URL(
    'https://cdn.jsdelivr.net/npm/@blazediff/[email protected]/wasm/blazediff_bg.wasm',
  ),
);

Pin the version (@7.0.0). It must match the installed package, because the JS glue and the .wasm change together. unpkg.com/@blazediff/[email protected]/wasm/blazediff_bg.wasm works identically.

Bundlers (Vite, Webpack 5+, esbuild, Rollup with plugin)

The new URL(asset, import.meta.url) pattern is bundler-aware: the asset is emitted into the build output and the URL is rewritten at build time:

import { initBlazediff } from '@blazediff/core-wasm';

const wasmUrl = new URL(
  '@blazediff/core-wasm/wasm/blazediff_bg.wasm',
  import.meta.url,
);
await initBlazediff(wasmUrl);

Node from the local filesystem

Offline, no CDN dependency. Read the bytes and pass them in:

// ESM (Node 20.6+):
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { initBlazediff } from '@blazediff/core-wasm';

const wasmPath = fileURLToPath(
  import.meta.resolve('@blazediff/core-wasm/wasm/blazediff_bg.wasm'),
);
await initBlazediff(readFileSync(wasmPath));
// CommonJS:
const { readFileSync } = require('node:fs');
const { initBlazediff } = require('@blazediff/core-wasm');

const wasmPath = require.resolve(
  '@blazediff/core-wasm/wasm/blazediff_bg.wasm',
);
await initBlazediff(readFileSync(wasmPath));

API

initBlazediff(input?)

Initializes the wasm module. Safe to call multiple times; subsequent calls return the cached promise. Accepts a URL, Response, ArrayBuffer, Uint8Array, or compiled WebAssembly.Module. Without input, the default --target web glue fetches the sibling blazediff_bg.wasm via import.meta.url (works in browsers; other runtimes need an explicit input, see above).

Returns: Promise<void>

diff(a, b, options?)

Compares two RGBA images. Takes and returns the same types as @blazediff/core's diff, which they are imported from.

Parameter Type Description
a, b Image { data, width, height } with RGBA8 pixels (width * height * 4 bytes). Browser ImageData fits as is
options DiffOptions Comparison options (optional)

Returns: Promise<DiffResult>

Option Type Default Description
threshold number 0.1 Color difference threshold (0-1). Lower is stricter
antialiasing boolean true Detect anti-aliased pixels and leave them out of the count
metric "yiq" | "oklab" "yiq" Color difference metric. oklab gives the same counts as pixelmatch 8
mask Uint8Array - One byte per pixel (width × height). Non-zero pixels are skipped
window number - Also report the N×N window with the most diff pixels
output Image - Draw the diff into this image in place. Identical images leave it unwritten
render { alpha?, diffColor?, diffColorAlt?, aaColor?, background? } - Output styling. background: "transparent" draws the diff alone
type DiffResult =
  | {
      kind: "compared";
      count: number;
      percentage: number;
      identical: boolean;
      /** Top-left corner and count of the busiest window, when `window` is set. */
      window?: { x: number; y: number; count: number };
    }
  | { kind: "dimension-mismatch"; a: Size; b: Size };

Invalid options or buffers reject with a BlazeDiffError that has a code: invalid-image, invalid-output, invalid-mask or invalid-option.

const result = await diff(a, b, {
  output: { data: out, width, height },
  render: { diffColorAlt: [0, 128, 255] },
});

Region analysis — what changed rather than where — is no longer part of this package. It lives in @blazediff/interpret-native, which is a Node N-API package and has no wasm build.

Usage

Browser

Decode images via createImageBitmap + OffscreenCanvas (or the ImageDecoder API), then pass the RGBA buffer to diff():

import { diff, initBlazediff } from '@blazediff/core-wasm';

await initBlazediff();

async function toRgba(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);
  return ctx.getImageData(0, 0, bitmap.width, bitmap.height);
}

const a = await toRgba('/baseline.png');
const b = await toRgba('/current.png');
const output = new ImageData(a.width, a.height);

const result = await diff(a, b, { output, threshold: 0.1 });

if (result.kind === 'compared') console.log(`${result.count} pixels differ`);

Node

import { readFileSync } from 'node:fs';
import { diff, initBlazediff } from '@blazediff/core-wasm';
import { PNG } from 'pngjs';

await initBlazediff(
  new URL(
    'https://cdn.jsdelivr.net/npm/@blazediff/[email protected]/wasm/blazediff_bg.wasm',
  ),
);

const a = PNG.sync.read(readFileSync('baseline.png'));
const b = PNG.sync.read(readFileSync('current.png'));
const result = await diff(a, b);

Performance

vs pixelmatch on M1 Max, image I/O excluded (pre-decoded RGBA buffers):

Fixturepixelmatchcore-wasmImprovement
4k/1338.53ms34.42ms89.8%
4k/3415.01ms45.34ms89.1%
page/2511.88ms70.89ms86.2%
blazediff/312.08ms5.76ms52.3%
pixelmatch/10.57ms0.11ms80.5%

Average ~51% faster across the full fixture set (~60% across differing pairs). Counts agree with pixelmatch within ~0.05% (e.g. 4k/1: 69 932 vs 69 912 of 17 920 000 pixels). Full benchmarks in BENCHMARKS.md.

Algorithm

Same two-pass block-based approach as @blazediff/core-native, recompiled for wasm:

  1. Cold pass: scans the image in 8x8 blocks using 32-bit integer comparison to identify changed regions
  2. Hot pass: only processes blocks marked as changed, applying YIQ perceptual color difference
  3. SIMD: v128 intrinsics (f32x4_*, i32x4_*) for parallel 4-lane RGBA extraction, alpha blend, YIQ transform, and threshold compare. Baseline simd128 has no native FMA, so weighted sums use add(mul, c)
  4. Anti-aliasing: Vysniauskas (2009) algorithm to detect AA artifacts

Picking the right package

Use casePackage
Browser, edge worker, wasm host@blazediff/core-wasm
Node CLI / server with native bin@blazediff/core-native
Pure JS / no wasm support@blazediff/core

References

版本与发布记录

@blazediff/core-wasm 共发布 13 个版本,首次发布于 2026/05/13;过去 12 个月发布了 13 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
7.0.0latest2026/11/053 天前
6.0.22026/10/2612 天前
6.0.12026/08/272 个月前
6.0.02026/08/172 个月前
5.4.02026/08/033 个月前
5.3.02026/07/163 个月前

关于 @blazediff/core-wasm 的常见问题

@blazediff/core-wasm 是什么?

WebAssembly build of blazediff for browsers, edge runtimes, and any wasm host. Same algorithm as @blazediff/core-native.

如何安装 @blazediff/core-wasm?

在项目目录运行 npm install @blazediff/core-wasm,或使用 pnpm add / yarn add / bun add。

@blazediff/core-wasm 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@blazediff/core-wasm 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@blazediff/core-wasm 可以商用吗?

@blazediff/core-wasm 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@blazediff/core-wasm 的下载量有多少?

根据 npm 官方下载统计,@blazediff/core-wasm 在 2026/10/09 – 2026/11/07 这 30 天内被下载 12,720 次,日均约 424 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@blazediff/core-wasm 还在维护吗?

最新版本 7.0.0 发布于 2026/11/05(3 天前);至今共发布 13 个版本,其中过去 12 个月发布了 13 个;首次发布于 2026/05/13。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。