Floyd-Steinberg vs Ordered Dithering: Which One Your Image Needs

COMPARISONDESIGNER

Floyd-Steinberg diffuses error into neighboring pixels; ordered dithering reads a fixed matrix instead. Which one to pick for a photo vs. an animation.

01/ ARTICLE

What's the actual difference between Floyd-Steinberg and ordered dithering?

Floyd-Steinberg dithering is an error-diffusion algorithm. It rounds each pixel to black or white, then pushes the rounding error into the pixels next to it, so the mistake gets corrected on the next few pixels instead of piling up. Ordered dithering works the opposite way: it compares each pixel to a fixed value from a tiled threshold matrix, almost always a Bayer matrix, and rounds accordingly, with no error carried anywhere. One algorithm remembers what it just did. The other doesn't. That's the whole mechanical split.

Everything downstream follows from that single difference. Floyd-Steinberg's memory is what lets it adapt to the image and look closer to the source on a still photo, and it's also what causes its one real problem: streaky "worm" artifacts in flat regions. Ordered dithering's lack of memory makes it fast, parallel, and rock-stable across video frames, but it also means it always shows the same crosshatch pattern no matter what's underneath it. Here's exactly how each one works, the numbers behind them, and which one actually fits your image.

How does Floyd-Steinberg's error diffusion actually work?

The exact coefficients

Floyd-Steinberg scans an image left to right, top to bottom. For every pixel, it rounds the value to the nearest available color (usually black or white for a 1-bit output), measures how far off that rounding was, and pushes that leftover error forward to four neighbors it hasn't processed yet, using four fixed weights:

  • Right pixel: 7/16
  • Below pixel: 5/16
  • Below-left pixel: 3/16
  • Below-right pixel: 1/16

Those numbers aren't arbitrary, and they aren't tunable in any meaningful sense. They were found by trial and error, specifically so a flat 50% gray region comes out looking like a checkerboard rather than a random mess. Because the error keeps getting redistributed instead of discarded, small mistakes in any one pixel average out across the image rather than piling up in one spot.

One detail worth flagging if you're comparing your own output against a reference: Floyd-Steinberg should run on linearized pixel values, not raw sRGB-gamma values. Feed it gamma-encoded data directly and it skews where the mid-tones land, throwing off the checkerboard balance the coefficients were tuned for.

Serpentine scanning

The default scan direction is left-to-right on every row, which can push errors in a consistent direction and make streaking worse. Serpentine scanning, also called boustrophedon scanning, alternates the direction every row: left-to-right, then right-to-left, then back. That breaks up the directional bias. It's the single most effective tweak to the base algorithm, and most serious dithering tools expose it as a toggle.

Want the lighter-weight sibling of this algorithm? Same error-diffusion idea, a smaller two-neighbor kernel. See the Atkinson dithering breakdown, which trades some of Floyd-Steinberg's accuracy for a cleaner, lower-contrast look.

How does ordered (Bayer) dithering's threshold matrix work?

The matrix, with real numbers

Ordered dithering compares every pixel against a corresponding cell in a small, fixed matrix tiled repeatedly across the whole image. The comparison itself is a simple offset-then-round: shift the pixel's value by the matrix cell (scaled to a small range), then snap to the nearest available palette color. No pixel's outcome depends on any other pixel.

The most common family is the Bayer matrix. The 2x2 version:

[0  2]
[3  1]   (scaled by 1/4)

And the 4x4 version, the one most tools default to:

[ 0   8   2  10]
[12   4  14   6]
[ 3  11   1   9]
[15   7  13   5]   (scaled by 1/16)

An 8x8 variant (64 threshold values, scaled by 1/64) exists too, for a finer pattern. All of these matrices are square, with a side length that's a power of two, and all of them tile edge-to-edge with no visible seam.

Why the pattern repeats

Because there's no error carried between pixels, the exact same input value always produces the exact same output pattern, position for position, no matter what's next to it in the source image. Ordered dithering is a lookup. Error diffusion is a running correction. That's the entire mechanical difference from Floyd-Steinberg in two sentences.

Floyd-Steinberg vs ordered dithering, side by side

Floyd-Steinberg (error diffusion)Ordered / Bayer (threshold matrix)
MechanismDiffuses rounding error to 4 neighbors (7/16, 5/16, 3/16, 1/16)Compares each pixel to a fixed, tiled threshold cell
Visual signatureOrganic, grain-like texture that follows image contentFixed crosshatch pattern, same regardless of content
Depends on neighboring pixels?Yes, sequential, single-threadedNo, every pixel is independent and fully parallel
SpeedSlower (can't parallelize across cores)Faster (uses all available CPU cores)
Stability across video framesCan jitter or shift frame to frameRock-stable, no jitter
Worm artifactsYes, in flat or shadow regionsNever; no error propagation to correlate
Best forStatic photos, print, anything judged on fidelityAnimation, GIFs, pixel art, anything that tiles or loops

Why does Floyd-Steinberg get "worm" artifacts and Bayer never does?

In a flat or shadow region, Floyd-Steinberg's diffused error can end up pushed the same direction pixel after pixel. It doesn't fully resolve, so it keeps compounding until it's large enough to flip a pixel, and then the pattern starts over. The result is a visible line or streak instead of an even, random-looking texture — most noticeable in uncorrelated flat areas like shadows and skies.

The standard mitigations each trade the worm for a different, smaller problem. Serpentine scanning breaks the directional bias by flipping scan direction every row. Larger error kernels (Jarvis-Judice-Ninke, Stucki, Burkes) spread the same error across more neighbors so no single line gets overloaded. Some implementations add a small amount of random jitter to the diffusion weights or thresholds specifically to decorrelate the pattern. None of this eliminates worms outright; it just reduces how often you see them, at a small cost in extra noise or softer detail.

Bayer dithering is structurally immune to the whole failure mode. There's no error to diffuse, so there's nothing to correlate into a line. What it gets instead is the crosshatch pattern: visible, but at least predictable and identical every time.

Which one is actually faster to compute?

Floyd-Steinberg is inherently sequential. You can't compute pixel N's output until pixel N-1's diffused error has landed on it, which makes the algorithm single-threaded by design. Bayer ordered dithering has no such dependency. Every pixel's threshold lookup only needs that one pixel's value and its position in the tiled matrix, so it parallelizes across every available CPU core with zero extra work.

For a one-off still export, this rarely matters. It matters a lot for anything real-time: live VJ effects, a video filter running per frame, or a preview that needs to redraw as you drag a slider. Dithering video, or running an effect live on stage? Bayer's parallelism is the practical reason it's the default. The visual reason is secondary.

Which one should you actually use?

  • Static photo you're printing or posting: Floyd-Steinberg, serpentine on. You want the adaptive, closer-to-source look, and a one-off render can afford to be slower.
  • Anything that animates, loops, or tiles (GIF, sprite sheet, looping video): Bayer. Pick the matrix size by how visible you want the pattern — 2x2 or 4x4 for an obviously retro look, 8x8 or larger for something closer to a smooth gradient.
  • Riso or 1-bit print work: usually Bayer, for predictability across an entire print run, unless the brief specifically wants Floyd-Steinberg's organic grain.
  • Still not sure which one your image needs? The fastest way to find out is to look at both side by side, on your image, not a stock example.

Frequently Asked Questions

Is Floyd-Steinberg better than ordered dithering?

It depends on the job, not on which algorithm is objectively stronger. Floyd-Steinberg's error diffusion adapts to local detail and generally looks closer to the source image on a static photo, which is why it's the default in most editors. Ordered (Bayer) dithering gives up that adaptiveness for a fixed, repeating threshold pattern that never jitters between frames, compresses smaller, and runs faster. Printing or posting a still photo? Floyd-Steinberg usually wins on fidelity. Working in animation, video, or pixel art, where frame-to-frame stability matters more than per-pixel accuracy? Bayer wins.

Why does my Floyd-Steinberg dither have weird streaks or "worms"?

That's the classic error-diffusion failure mode. In flat or shadow regions, the same rounding error keeps getting pushed in the same direction until it's large enough to flip a pixel, then the pattern repeats, producing a visible line instead of even grain. Serpentine scanning (alternating scan direction every row) is the standard fix, and most dithering tools support it, including kott. It won't eliminate worms entirely. It just breaks up the directional bias enough that they're far less noticeable.

Can I combine Floyd-Steinberg and ordered dithering?

Not in the same pass. They're mutually exclusive quantization strategies for a given pixel, since one diffuses error forward and the other reads a fixed matrix cell. What you can do is pick per asset: run key hero images through Floyd-Steinberg for fidelity, and run anything that animates or tiles (GIFs, sprite sheets, looping video) through Bayer so it doesn't flicker.

What Bayer matrix size should I use?

Bigger isn't automatically better. A 2x2 or 4x4 Bayer matrix gives a coarse, chunky crosshatch that reads as intentionally retro, good for a Mac-1-bit or riso look. An 8x8 or larger matrix produces a finer, less obtrusive pattern that's closer to a smooth gradient at a glance, usually the better call if you want a clean reduced-palette image rather than a visible dither texture.

Does ordered dithering ever get worm artifacts?

No, and structurally it can't. Worms come from diffusing error between pixels; ordered dithering never diffuses anything, it just compares each pixel to a fixed threshold cell. What it gets instead is its own signature artifact: a visible, repeating crosshatch pattern that doesn't adapt to image content the way error diffusion does.

Conclusion

Floyd-Steinberg diffuses each pixel's rounding error into its neighbors. Ordered dithering checks each pixel against a fixed matrix cell and diffuses nothing. That one difference decides everything else: Floyd-Steinberg looks closer to your source but can streak and only runs single-threaded, while Bayer never streaks and runs on every core but always shows its crosshatch. Static photo, judged on fidelity? Reach for Floyd-Steinberg with serpentine on. Anything animated, tiled, or real-time? Reach for Bayer.

The fastest way to know which one your image actually needs is to run it through both and look. Open kott's /studio with a Floyd-Steinberg-vs-Bayer comparison preloaded on your own source image and see the difference on the thing you're actually shipping, not a stock example.

02/ OUT

Try it in the studio

Every setting described above is a real control. Open your own image and sweep it.

All journal entries