Quick start

Install denoiser and denoise your first image in a few lines.

Install

npm install denoiser

This one package is enough to get going: the default runtime has no runtime dependencies. onnxruntime-web is an optional peer dependency, needed only if you opt into the ORT runtime (denoiser/ort; see Choosing a runtime).

Note

Everything here was validated on Apple GPUs only. The default runtime is AutoRuntime: WebNN fp16 for base/large models when WebNN is available (Chrome with a feature flag), the hand-written WGSL runtime otherwise.

Hello world

This is the entire "noisy image in, clean image out" flow — no three.js, no build tricks. It's lifted directly from examples/hello-world/src/main.ts:

import { Denoiser } from 'denoiser';

async function run(): Promise<Denoiser> {
  const noisyImg = document.querySelector<HTMLImageElement>('#noisy-img')!;
  await noisyImg.decode();

  const denoiser = await Denoiser.create({ quality: 'balanced' });
  const denoised = await denoiser.denoise(noisyImg);

  const canvas = document.querySelector<HTMLCanvasElement>('#denoised')!;
  canvas.getContext('2d')!.putImageData(denoised!, 0, 0);

  return denoiser;
}

Three calls do the work:

  1. Denoiser.create() — creates the WebGPU device (or adopts the one you pass as device) and loads a model. quality picks its size: 'fast' (the default, the small network), 'balanced' (the base network: visibly cleaner, about 1.5x the time) or 'high' (the large network where one exists).
  2. denoiser.denoise(imageLike) — runs it. Accepts an HTMLImageElement, HTMLCanvasElement, ImageBitmap, ImageData, HTMLVideoElement, OffscreenCanvas, or a raw { data, width, height } object; returns ImageData.
  3. putImageData — paint the result.

Live demo → — the deployed page shows this exact source next to the noisy/denoised comparison.

What happens on first run

Denoiser.create() fetches the weights it needs from the default CDN (tzaUrl, jsDelivr by default) — nothing ships inside the npm package itself. Weight files run about 0.6–7.7 MB depending on quality tier (see Models & weights for the full size table); only the one variant your call actually needs is fetched, not the full set. The fetched bytes are cached in memory for the life of the page (and by the browser's normal HTTP cache across page loads), so only the very first denoise() call pays the download.

Note

Production apps should host the weights themselves rather than rely on the default CDN URL — see Models & weights for the tzaUrl option.

Next