/ALGORITHM · FLOYD-STEINBERG
Floyd-Steinberg Dithering
By Kailash · Updated October 9, 2026 · 6 min read
The short answer
Floyd-Steinberg dithering is the error-diffusion algorithm published by Robert Floyd and Louis Steinberg in 1976, and it is still the default almost everywhere. Each pixel is rounded to the nearest available colour, and the error that rounding introduced is pushed onto four neighbours that have not been drawn yet, in the ratio 7, 3, 5, 1 over sixteen. Because the weights add up to exactly 1, nothing is thrown away, which is why it holds more detail than any of the alternatives and why its speckle looks slightly organic rather than patterned.
The kernel
The whole algorithm is this table. X is the pixel just drawn, and the fractions are how much of its error each neighbour absorbs:
X 7/16 3/16 5/16 1/16
Two details matter more than people expect. The first is that the error is carried in full precision, not rounded per step, so it accumulates across a run of similar pixels and eventually forces a flip. That accumulation is what creates texture in a flat area instead of a solid block. The second is that it only ever pushes error forward, to pixels not yet processed, which is what makes a single left-to-right, top-to-bottom pass sufficient.
Why it is still the default
Not, as is often claimed, because it conserves the full error. Stucki, Jarvis and Sierra all conserve it too: their weights sum to exactly 1 just as these do. Only Atkinson deliberately throws error away. What actually distinguishes Floyd-Steinberg is that it achieves full conservation with the smallest kernel of the family, four neighbours against Stucki's twelve. That makes it roughly three times cheaper per pixel, and it keeps the error local, so edges stay crisp rather than being smeared across two rows. You pay for that locality with the worm artefact below. If you are dithering because you have to rather than because you want the look, this remains the correct default.
The worm artefact, and what to do about it
The one real complaint about Floyd-Steinberg is worming: in smooth gradients the dots line up into short diagonal chains that the eye picks out as texture crawling across the image. It happens because the error always flows in the same direction on every row. Three fixes, in order of how often they work:
- Serpentine scanning. Process odd rows right to left so the error direction alternates. Breaks up most chains and costs nothing. Neither the tool on this page nor the full editor does this today, so it is one for your own implementation.
- Switch to Stucki. A wider kernel distributes the error over twelve neighbours instead of four, so no single direction dominates. Gradients come out cleaner, edges slightly softer.
- Add a little noise before dithering. Even a small amount decorrelates the error and the chains dissolve. Again, a pre-pass you would add yourself.
The algorithm, in about fifteen lines
Worth seeing written out, because the two things people get wrong are both visible here: the error is carried at full precision rather than rounded per step, and it only ever flows to pixels that have not been drawn yet.
for (let y = 0; y < h; y++) {
for (let x = 0; x < w; x++) {
const i = (y * w + x);
const old = buf[i];
const nw = nearest(old); // snap to the palette
buf[i] = nw;
const err = old - nw; // keep the remainder in full precision
if (x + 1 < w) buf[i + 1] += err * 7 / 16;
if (y + 1 < h) {
if (x > 0) buf[i + w - 1] += err * 3 / 16;
buf[i + w] += err * 5 / 16;
if (x + 1 < w) buf[i + w + 1] += err * 1 / 16;
}
}
}
Two notes that save an afternoon. Keep buf as floats, not bytes: clamping to 0–255 after every pixel destroys the accumulation that makes the texture. And do the palette snap on the accumulated value, not the original, which is why the read happens after the previous pixels have already written into this one.
When Floyd-Steinberg is the wrong answer
It is the default, not the universal choice. Four cases where something else wins:
- Video or animation. Error diffusion is derived from neighbours, so a one-pixel change in the source reshuffles the whole downstream pattern. Frame to frame that reads as boiling. Use an ordered matrix, which is fixed to the pixel grid and therefore stable. This is the single biggest reason to reach for Bayer instead.
- Anything running on a GPU. Each pixel depends on the one before it, so the algorithm is inherently sequential. Ordered dithering is one lookup per pixel and parallelises trivially.
- When you want the dithering to be visible as a style. Floyd-Steinberg tries to disappear. If the texture is the point, Atkinson's blown highlights or a coarse Bayer grid say it louder.
- Very small output. Below about 100px across, the irregular speckle reads as noise rather than tone. A 4×4 ordered matrix holds its shape better at icon sizes.
Floyd-Steinberg against the alternatives
- Against Atkinson: Floyd-Steinberg keeps highlight and shadow detail; Atkinson deliberately blows both out. Atkinson reads as a style, Floyd-Steinberg reads as a faithful reduction.
- Against Bayer: Bayer compares every pixel against a fixed repeating matrix and produces a visible regular crosshatch. Floyd-Steinberg produces an irregular speckle. If the grid pattern is the point, use Bayer; if it is a distraction, use this.
- Against Stucki: Stucki is Floyd-Steinberg with a bigger, gentler kernel. Better gradients, slightly less bite on edges, noticeably slower.
Try it on your own image
Floyd-Steinberg is already selected below. Drop a photo in and switch the algorithm to compare the same source across all seven. Everything runs in your browser, nothing is uploaded.
Controls
This runs the same kernels and palette values as the full editor. Switch the algorithm above to compare Floyd-Steinberg against the others on your own image.
Frequently asked questions
What is Floyd-Steinberg dithering?
What is the Floyd-Steinberg matrix?
Why does Floyd-Steinberg produce worm patterns?
Floyd-Steinberg or Atkinson, which is better?
Is Floyd-Steinberg still used today?
Can I apply Floyd-Steinberg dithering online for free?
Floyd-Steinberg is one of fifteen dither algorithms in the engine. The complete guide to dithering puts the error-diffusion family side by side with the ordered one, and the dither tool runs all of them on your own image with the full palette set.
