Blue noise dithering: why it beats Bayer and Floyd–Steinberg

Blue noise is ordered dithering without the grid: what blue noise means, how a void-and-cluster mask is built, and when to pick it over Bayer.

Updated

Lion photo dithered with Blue noise

Sample render. Slide to compare the photo with the result. Try the algorithm live below.

Blue noise

Rendered in 0.0 ms on your machine. Every dot here is what a pen or laser would put down.

Blue noise dithering is ordered dithering with a threshold pattern that has no visible structure. Instead of a tiled Bayer grid, each pixel is compared against a precomputed noise mask whose energy is all at high spatial frequencies, so the dots come out evenly spread with no clumps, no grid and no worms. It costs one comparison per pixel, runs in parallel, holds still between animation frames, and looks better than Bayer at every tone. The only thing it needs is a good mask, and those are free.

Run blue noise dithering on your own image.

What “blue” means

Noise is described by its spectrum, the same way light is. White noise has equal energy at all frequencies; a field of random dots has blotches at every scale, including the large ones your eye sees as uneven tone. Blue noise has its energy pushed to the high frequencies, the way blue light has more energy than red. In a blue-noise dot pattern there are no large-scale variations in density: every neighbourhood of a few dots has the same number of dots as every other, and the randomness is only in exactly where inside that neighbourhood each dot sits.

Robert Ulichney coined the term for halftoning in his 1987 book Digital Halftoning and showed that the output of a well-behaved error-diffusion algorithm has this spectrum. That is why Floyd–Steinberg looks good: it produces blue noise by accident, most of the time. Blue-noise dithering produces it on purpose, every time.

Why the grid and the worms go away

An n×n Bayer matrix repeats every n pixels, so its output has a strong spike in the spectrum at that period, which you see as crosshatching. Error diffusion has no period but carries the error along the scan direction, which produces worms in flat areas and checkerboard patches at 1/2, 1/3 and 1/4 grey.

A blue-noise mask has neither. It is aperiodic across its tile, so there is no crosshatch, and every pixel is decided independently, so there is no scan-direction memory and nothing to form a worm. At any grey level, thresholding the mask produces a dot pattern whose spectrum is blue, because the mask was built to guarantee it.

How a mask is made: void-and-cluster

Ulichney’s void-and-cluster method (Proc. SPIE 1913, 1993) builds a mask in which every threshold slice is blue noise. The idea is that a “cluster” is a place where dots are too close together and a “void” is a place where they are too far apart, and both are found by blurring the dot pattern with a Gaussian.

  1. Start with a random binary pattern of about 10% ones on an N×N tile that wraps at the edges.
  2. Blur it (Gaussian, σ about 1.5–1.9, wrapping). The brightest pixel of the blur is the tightest cluster; the darkest is the largest void.
  3. Move a dot from the tightest cluster to the largest void. Repeat until moving makes it worse. This gives an even initial pattern.
  4. Rank downwards: repeatedly remove the dot at the tightest cluster and give that pixel the next lower rank.
  5. Rank upwards: starting again from the initial pattern, repeatedly add a dot at the largest void and give it the next higher rank, until the tile is full.
  6. Scale the ranks to 0–1. The result is a threshold mask: to dither, output white where pixel > mask[x mod N][y mod N].

In pseudocode, the growing half:

mask = initial_pattern.copy()          # ones and zeros, N x N
rank = zeros(N, N)
r = count_ones(mask)
while r < N * N:
    energy = gaussian_blur_wrap(mask, sigma=1.9)
    y, x = argmin(energy where mask == 0)  # the largest void
    mask[y, x] = 1
    rank[y, x] = r
    r += 1
threshold = (rank + 0.5) / (N * N)

A 64×64 mask takes about a minute with a naive blur and a few seconds with an FFT; a 128×128 mask is the usual size for images because the tile repeat is invisible at that scale. Plotcaster ships a 128×128 mask generated this way. If you want ready-made masks, Christoph Peters publishes void-and-cluster textures from 16² to 1024², 8-bit and 16-bit, under CC0 at momentsingraphics.de.

Blue noise vs Bayer vs Floyd–Steinberg

Blue noiseBayer 8×8Floyd–Steinberg
FamilyOrderedOrderedError diffusion
Cost per pixel1 lookup + compare1 lookup + compare4 multiply-adds
Parallel / GPUYesYesNo
Distinct tones16,385 (128² + 1)65Continuous
Visible structureFine even grainCrosshatch gridWorms, patches
Stable under animationYesYesNo
Tiles seamlesslyYesYesn/a
Sharpening of edgesNoneNoneSome
Tone accuracyGoodGoodBest
Pen-plotter dotsEven spacingRegular rowsRuns and streaks

Blue noise loses to error diffusion on one count: fine detail. Error diffusion sharpens edges and text; a blue-noise dither reproduces them exactly as soft as they were. On a portrait that is usually an improvement. On small text it is not.

The Obra Dinn story

Lucas Pope’s Return of the Obra Dinn (2018) is a 3D game rendered entirely in two colours, in the style of a 1984 Macintosh. Pope’s devlog describes trying an 8×8 Bayer matrix first, then a 128×128 blue-noise field, and keeping both: Bayer for characters and objects, where the ordered grid gives a clean, deliberate look, and blue noise for environments, where its lack of structure survived screen scaling and video compression far better. The remaining problem was that a screen-space dither pattern shimmers when the camera moves, which made players motion-sick at full screen; he solved it by anchoring the pattern to the camera’s rotation so it turns with the view, and by a motion-blur-like treatment of the dither. Surma’s Ditherpunk article, which reverse-engineers the technique for the web, is the best long read on the subject.

Where blue noise wins

  • Animation and video. No frame-to-frame crawl, because each pixel’s threshold never changes.
  • GPUs and shaders. One texture fetch; the standard way to hide banding in fog, shadows and gradients.
  • E-ink and low-resolution 1-bit displays. Hides the pixel grid without adding a second grid.
  • Pen plotters. Every dot is a pen mark. Blue noise gives even spacing with no touching dots and no streaks, which plots cleanly and looks like hand stippling. The stipple style takes the idea off the grid entirely.
  • Laser engraving. No worms to burn as lines, and even coverage that dot gain does not turn into blotches.
  • Pixel art from photos. When Bayer looks too mechanical and Floyd–Steinberg too messy. See Dithering in pixel art.

Where it does not

  • Text and line art. Use error diffusion or a plain threshold; blue noise softens edges.
  • Very small images. Below about 64 pixels the grain competes with the picture.
  • Maximum tone accuracy. Error diffusion, especially Ostromoukhov, tracks tone more closely in gradients. The difference is small and only visible side by side.

Do it right: linear light

Threshold against the mask in linear light, not sRGB. A 50% sRGB pixel is about 21% linear; compared against a uniform mask in sRGB it comes out far too bright once printed. Convert first, then threshold, then apply dot gain for the output device. Plotcaster does this when Tone matching is set to Linear, and Perceptual is there for when you want the screen-matching result instead. The comparison guide covers tone handling for all sixteen algorithms.

Try it

Load a photo with blue noise dithering, then switch to Bayer 4×4 and Floyd–Steinberg to see the grid and the worms come and go. Export a 1-bit PNG at your printer’s DPI or an SVG with each dot as a pen mark.

FAQ

What is the difference between blue noise and white noise dithering? White noise is random at every frequency and produces blotchy, uneven tone. Blue noise has no low-frequency energy, so tone is even and only the fine placement of dots is random.

Is blue noise dithering better than Floyd–Steinberg? For animation, GPUs, plotters and lasers, yes. For maximum tonal accuracy on a still photo at screen resolution, Floyd–Steinberg or Ostromoukhov is marginally better.

Why does the mask need to tile? Because it is repeated across the image. Void-and-cluster wraps its blur at the edges so that the repeat is invisible; a mask made without wrapping shows a seam every tile.

How big should a blue noise mask be? 64×64 is enough for real-time graphics that pick a different tile each frame. 128×128 is the usual choice for still images and print; larger sizes buy nothing visible.

Can I make a blue noise mask by filtering white noise? You can high-pass-filter white noise, but the result falls apart when thresholded: the slices are not blue at every level. Void-and-cluster ranks the pixels so that every slice is.

Where can I download blue noise textures? momentsingraphics.de publishes CC0 void-and-cluster masks in sizes from 16² to 1024². Plotcaster has one built in and needs no download.