Art studio with a canvas showing a sharp-to-blurred gradient and a red cat on a stool

StackBlur.js: The Blazing Fast Canvas Blur That Skips the Math Homework

The Orange Cat
The Orange Cat

Every web developer eventually encounters the moment when they need to blur something on a canvas. Maybe it is a frosted glass overlay, a privacy filter over sensitive text, or a dreamy background behind a profile card. The obvious first instinct is CSS filter: blur(), but that only works on DOM elements. The moment you need to manipulate actual pixel data, draw blurred content into a canvas, or run the operation server-side, you need a different tool. That tool, for over a decade and half a million weekly downloads, has been StackBlur.js.

stackblur-canvas implements the StackBlur algorithm invented by Mario Klingemann around 2004. The algorithm produces results visually similar to a true Gaussian blur but runs in constant time per pixel regardless of the blur radius. It achieves this by maintaining a stack data structure that allows incremental updates as the blur kernel slides across the image, avoiding the expensive per-pixel multiplications that make Gaussian blur slow at large radii. The result is a library that weighs roughly 2-3 KB gzipped, has zero dependencies, and ships five functions that cover every canvas blur scenario you are likely to encounter.

What Makes It Fast

Traditional Gaussian blur is O(r) per pixel, where r is the blur radius. Double the radius, double the work. StackBlur sidesteps this entirely. It processes the image in two passes (horizontal and vertical, like a separable Gaussian filter) and uses push/pop operations on a weighted stack to update the running sum incrementally. The amortized cost per pixel is O(1) no matter how large the radius gets. A blur radius of 5 and a blur radius of 50 take essentially the same time per pixel.

The visual difference between StackBlur and true Gaussian blur is negligible at moderate to large radii. At very small radii (1-3 pixels) a trained eye might notice slight differences, but for the vast majority of UI effects, the output is indistinguishable.

Installation

npm install stackblur-canvas
yarn add stackblur-canvas

Version 3.0.0 ships as native ESM with a "type": "module" declaration and dual ESM/CJS exports, so it works in all modern environments out of the box.

One-Line Image Blur

Blurring an Image onto a Canvas

The simplest use case is taking an existing image element and rendering a blurred version onto a canvas.

import * as StackBlur from 'stackblur-canvas';

const img = document.getElementById('photo') as HTMLImageElement;
const canvas = document.getElementById('output') as HTMLCanvasElement;

StackBlur.image(img, canvas, 20);

The third argument is the blur radius. Higher values produce a stronger blur. The function draws the image onto the target canvas and applies the blur in place. By default it only processes the RGB channels, leaving alpha untouched. Pass true as the fourth argument to blur the alpha channel as well.

StackBlur.image(img, canvas, 20, true);

One thing to watch out for: the image() function is subject to CORS restrictions. If the image is loaded from a different origin and the server does not send the appropriate CORS headers, the canvas will be tainted and the blur will fail.

Blurring a Canvas Region

When you already have content drawn on a canvas and want to blur a specific rectangular region, canvasRGB and canvasRGBA are the functions to reach for.

import { canvasRGB, canvasRGBA } from 'stackblur-canvas';

const canvas = document.getElementById('scene') as HTMLCanvasElement;

// Blur only the RGB channels in a 200x200 region starting at (50, 50)
canvasRGB(canvas, 50, 50, 200, 200, 15);

// Blur all four channels (RGBA) across the entire canvas
canvasRGBA(canvas, 0, 0, canvas.width, canvas.height, 25);

The distinction between RGB and RGBA matters when your canvas has transparency. If you are compositing layers and want the blur to affect opacity, use canvasRGBA. If you want the blur to soften colors without touching the alpha mask, stick with canvasRGB.

Working with Raw Pixels

Off-Screen Blur with ImageData

The imageDataRGB and imageDataRGBA functions operate directly on ImageData objects rather than canvas elements. This is the key to unlocking two powerful patterns: off-screen processing and Web Worker blur.

import { imageDataRGBA } from 'stackblur-canvas';

const canvas = document.getElementById('editor') as HTMLCanvasElement;
const ctx = canvas.getContext('2d')!;

// Grab the pixel data
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

// Blur the raw pixel data
imageDataRGBA(imageData, 0, 0, canvas.width, canvas.height, 30);

// Put it back
ctx.putImageData(imageData, 0, 0);

Because ImageData is a transferable object, you can send it to a Web Worker, blur it off the main thread, and transfer it back without blocking the UI.

Blur in a Web Worker

// worker.ts
import { imageDataRGBA } from 'stackblur-canvas';

self.onmessage = (event: MessageEvent) => {
  const { imageData, width, height, radius } = event.data;
  imageDataRGBA(imageData, 0, 0, width, height, radius);
  self.postMessage({ imageData }, [imageData.data.buffer]);
};
// main.ts
const canvas = document.getElementById('preview') as HTMLCanvasElement;
const ctx = canvas.getContext('2d')!;
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

const worker = new Worker(new URL('./worker.ts', import.meta.url), {
  type: 'module',
});

worker.postMessage(
  {
    imageData,
    width: canvas.width,
    height: canvas.height,
    radius: 40,
  },
  [imageData.data.buffer]
);

worker.onmessage = (event: MessageEvent) => {
  ctx.putImageData(event.data.imageData, 0, 0);
};

Transferring the underlying ArrayBuffer means the data moves to the worker without copying, keeping memory usage flat. This pattern is especially useful for photo editing tools or any application where blur operations need to feel instant.

Building a Glassmorphism Effect

A common UI pattern is the frosted glass card: a semi-transparent panel over a blurred version of the background. Here is how to build one entirely with canvas.

import { canvasRGB } from 'stackblur-canvas';

function createFrostedBackground(
  sourceCanvas: HTMLCanvasElement,
  x: number,
  y: number,
  width: number,
  height: number,
  radius: number
): HTMLCanvasElement {
  const frosted = document.createElement('canvas');
  frosted.width = width;
  frosted.height = height;
  const ctx = frosted.getContext('2d')!;

  // Copy the region from the source
  ctx.drawImage(sourceCanvas, x, y, width, height, 0, 0, width, height);

  // Apply the blur
  canvasRGB(frosted, 0, 0, width, height, radius);

  // Add a white tint for the glass look
  ctx.fillStyle = 'rgba(255, 255, 255, 0.15)';
  ctx.fillRect(0, 0, width, height);

  return frosted;
}

const scene = document.getElementById('scene') as HTMLCanvasElement;
const glass = createFrostedBackground(scene, 100, 100, 300, 200, 25);

This gives you a blurred snapshot of whatever is behind the card position, with a subtle white overlay for the characteristic frosted appearance. Because canvasRGB mutates the canvas in place, the operation is fast and allocation-free.

The Full API at a Glance

The entire public surface of stackblur-canvas fits on an index card:

Function Input Blurs Alpha
image() HTMLImageElement + Canvas Optional
canvasRGB() Canvas region No
canvasRGBA() Canvas region Yes
imageDataRGB() ImageData No
imageDataRGBA() ImageData Yes

That is it. Five functions, no configuration objects, no builder patterns, no plugin system. Every function mutates its input in place and returns nothing. The radius parameter is always an integer.

When to Reach for Something Else

StackBlur is not the right tool for every job. If you need to blur a DOM element for a visual effect, CSS filter: blur() is faster and simpler since it runs on the GPU. If you need a mathematically precise Gaussian blur for scientific imaging, SVG's feGaussianBlur filter is more accurate. And if you are already using a WebGL framework like pixi.js, its built-in blur filter will outperform any CPU-based approach by orders of magnitude.

But if you are working with canvas pixel data, need server-side blur with node-canvas, want zero dependencies, or need to run blur operations in a Web Worker, StackBlur occupies a sweet spot that nothing else quite matches. Ten years of stability, half a million weekly downloads, and an algorithm so elegant it has been ported to nearly every programming language in existence speak for themselves.