StackBlur.js: The Blazing Fast Canvas Blur That Skips the Math Homework
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.