# stackblur-canvas, a fast approximate Gaussian blur for the canvas

> StackBlur is a JavaScript port of Mario Klingemann's canvas blur, and this repository maintains it as a modern package with typed entry points, Rollup builds and dual module support. The name is the first thing to sort out, because the repository is StackBlur and the package is stackblur-canvas.

**flozz/StackBlur** — Fast and almost Gaussian blur

- Repository: https://github.com/flozz/StackBlur
- Stars: 1,311 · Forks: 119
- Language: JavaScript
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/flozz-stackblur

## The algorithm, the author, and why it is called almost Gaussian

StackBlur.js is a fast, almost Gaussian blur created by Mario Klingemann, and the project documentation credits the original source and the original author's own pages, including the fast blur page on the quasimondo site and a demo of the canvas implementation.

The phrase almost Gaussian is the important part of that description. A true Gaussian blur is defined by a kernel whose weights follow a bell curve, and computing it directly means a large number of multiply and add operations per output pixel. The stack based approach approximates that result with a much cheaper sequence of running sums, which is the entire point of the library: an image that looks blurred to the eye, computed fast enough to run on an animation frame.

That trade is worth naming precisely, because it is a real trade and not a detail. You are getting a visually similar result, not an identical one, and the approximation is designed for the case where the difference is invisible at the radius and the performance budget you have. If the output is going to be measured rather than looked at, the approximation is not free.

For the audience, the use case is a web page that needs a blurred background, a frosted glass panel, or a soft shadow, on a canvas rather than with a CSS filter, and where the blur has to be redrawn as the content changes.

## Five entry points, and the choice is about the source type

The API is five functions, and which one you call depends on what you already have in your hands.

For a source image, there is a single call that does everything.

```js
StackBlur.image(sourceImage, targetCanvas, radius, blurAlphaChannel);
```

The source is an HTMLImageElement or its id, the target is an HTMLCanvasElement or its id, the radius is the radius of the blur, and the fourth argument is optional and defaults to false, and you set it to true if you want to blur an RGBA image.

The other four take the source data you already have and blur a rectangle of it. The canvas variants take a target canvas, the coordinates of the top left corner of the rectangle to blur, its width and height, and the radius. The ImageData variants take the canvas ImageData with the same rectangle and radius arguments.

```js
StackBlur.canvasRGBA(targetCanvas, topX, topY, width, height, radius);
StackBlur.canvasRGB(targetCanvas, topX, topY, width, height, radius);
StackBlur.imageDataRGBA(imageData, topX, topY, width, height, radius);
StackBlur.imageDataRGB(imageData, topX, topY, width, height, radius);
```

The RGB and RGBA split matters because it is the difference between blurring the colour channels and also blurring the alpha channel. A background blur on a transparent canvas that is blurred as RGB will keep a hard edge where the alpha changes, and the alpha aware variant is what fixes that.

The rectangle arguments are the feature most people do not know about. Blurring a sub rectangle rather than the whole surface is what lets you run this on a moving element without paying for the entire canvas, and in a live application it is the difference between a per frame cost of a few thousand pixels and a few hundred thousand.

The documentation points to a JSDoc build for the full reference, generated from the source.

## Three ways to load it, and a package name that is not the repository name

The consumption paths are straightforward, but the naming needs care.

The repository is StackBlur, and the published package is stackblur-canvas, at version 3.0.1, with a description of Fast and almost Gaussian blur by Mario Klingemann. Anyone upgrading from the old script has to change the name they install.

The standalone path is the classic one: download the latest zip from GitHub or clone the repository, and include the dist file in your page.

```bash
git clone git@github.com:flozz/StackBlur.git
```

```html
<script src="StackBlur/dist/stackblur.js"></script>
```

The Node path installs the npm package and requires it.

```bash
npm install --save stackblur-canvas
```

```js
const StackBlur = require('stackblur-canvas');
```

The browser path with modern bundlers uses ES modules, either by pointing at the ES module build in node_modules directly, or, if you use Rollup, by adding the node-resolve plugin and importing by package name.

```js
import * as StackBlur from 'stackblur-canvas';
```

The package metadata supports both worlds properly. The type field says module, the main field points at the CommonJS build, and the exports map has separate import and require conditions, each with its own types file, index.d.ts for the ES module side and index.d.cts for the CommonJS side. That is the correct shape for a package consumed by both, and it is the reason the type declarations do not get lost in one of the two cases.

The package's browserslist configuration sets a target of cover 100%.

## Building with Rollup, and the demo you can run locally

The library is built with Rollup, and the rule is simple: if you change something in the src folder, run the rollup script to rebuild the files in the dist folder.

```bash
npm run rollup
```

The configuration is a rollup.config.js at the root, and the toolchain includes the Rollup Babel plugin and the terser plugin, so the bundle is transpiled and minified, with the minified and ES module variants both produced for the different consumption paths described above.

There is a demo in the repository, with its own HTML, CSS and JavaScript, and a start script that opens it in a browser on a local server.

That script opens the demo at localhost:8081 and serves it, and there is a parallel open-docs script that regenerates the JSDoc output and serves that as well. The docs script is worth noting because it runs jsdoc in pedantic mode against a dedicated configuration file, which means the documentation build fails on malformed comments rather than producing broken output quietly.

The repository also has a pnpm workspace configuration and a pnpm lock file, along with a Babel configuration, an ESLint flat configuration and an editor configuration. The lint script runs ESLint over JavaScript, CommonJS, markdown and HTML files, which is an unusually broad scope and reflects a repository where the documentation and the demo are treated as code.

The CHANGES.md file at the top level is where the release history lives, since the project publishes no GitHub releases.

## The test script that fails on purpose, and the packaging check that matters

Two scripts in the package manifest are worth reading closely, because they tell you what the project does and does not guarantee.

The first is the test script.

```
"test": "npm run eslint && npm run rollup && echo \"Error: no test specified\" && exit 1"
```

It lints the code, rebuilds the bundle, and then fails on purpose with the default placeholder message. There are no unit tests in this repository. The command is not a test suite, it is a build check that ends in a deliberate failure, which means running npm test is a way to verify the code lints and bundles, not a way to verify behaviour.

The second is the packaging check, and it is the more interesting one. The prepublishOnly script runs attw, the are the types wrong tool, against the packed output, and then installs with pnpm. The attw tool checks a package's type declarations against the way consumers actually resolve them, across the import and require conditions and the different TypeScript module settings.

For a library that publishes a types file for each of its two entry conditions, running that check before publishing is exactly the right habit, and it is a check that catches a class of bug that only appears in a consumer's project: the declaration file exists, but the exports map points at the wrong one, so TypeScript sees no types at all.

The result is a package that is careful about how it is consumed and not at all careful about whether the algorithm is correct for every input, and knowing which of those two risks you are taking on is the point of reading these scripts.

## MIT licensed, with a version requirement nobody should take literally

The licence is MIT, in a LICENSE-MIT.txt file, and the author field credits Mario Klingemann with Brett Zamir listed as a contributor. The original work is credited in the documentation as well, with the original source and the algorithm write-up both linked, which is the right thing to do for a port of someone else's technique.

The keywords are stackblur, blur, canvas and gaussian, and the homepage points at the canvas demo on the author's site rather than at the repository, which is a small detail that dates the package and reminds you that the documentation lives elsewhere.

The engines field says node >=0.1.16, which is not a real constraint in any practical sense and is almost certainly a joke or a placeholder that was never corrected. The real requirements are visible elsewhere: the build needs a modern Node, and the Rollup configuration with its Babel and terser plugins implies a recent toolchain.

Do not take the engines field as a statement that the package works on anything from Node 0.1 onwards, and do not use it as a reason to skip a version check.

The maintenance picture is modest. The last push was on 2026-09-17, the repository is not archived, and there are no GitHub releases, so the published version number in the package manifest is the only version marker a consumer sees. For a library whose algorithm has not changed in years, that is a reasonable state, but it means the changelog file and the npm version are where you look to know what changed.

## Conclusion

Adopt stackblur-canvas if you need a blur on a canvas or ImageData in the browser and want something quicker than a true Gaussian, particularly for a live or animated surface where the blur runs every frame. Do not adopt it if you need a mathematically exact Gaussian, since the algorithm is explicitly an approximation, and do not expect unit tests, because the test script in the package manifest is a placeholder that exits with an error. Before you upgrade a copy of the old script, note that the published package is stackblur-canvas and not StackBlur, check the exports map to see whether the require and import paths resolve where your build expects them, and run the rollup build yourself if you are changing anything under src.

## FAQ

### How do I blur an image with stackblur-canvas?

Call StackBlur.image with the source image, the target canvas, the radius and an optional blurAlphaChannel flag set to true for an RGBA image. There are also canvas and ImageData variants for blurring a rectangle of an existing surface.

### What is the npm package name for StackBlur?

The published package is stackblur-canvas at version 3.0.1, even though the repository is named StackBlur. The package supports both CommonJS and ES module consumers through an exports map with separate type declarations for each.

### How do I rebuild the dist files after changing the source?

Run `npm run rollup`. The library is built with Rollup, with the Babel and terser plugins, and changes in the src folder need to be rebuilt before they appear in dist.

### Does stackblur-canvas have unit tests?

No. The test script in the package manifest lints the code, rebuilds the bundle and then exits with an error placeholder, so npm test is a build check rather than a test suite. The publish flow does run the are the types wrong check against the packed output.

### Is StackBlur an exact Gaussian blur?

No. It is described as a fast, almost Gaussian blur created by Mario Klingemann, an approximation of the true Gaussian result obtained with a cheaper stack based sequence of running sums.

## Sources

- [flozz/StackBlur on GitHub](https://github.com/flozz/StackBlur)
- [Issues](https://github.com/flozz/StackBlur/issues)
- [License: MIT](https://github.com/flozz/StackBlur/blob/master/LICENSE)
- [README](https://github.com/flozz/StackBlur/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/flozz-stackblur
