Look closely at a retro sky, a shaded metal plate, or a fading glow in an old game, and you will find tiny crosshatch patterns hiding in the gradients. That texture is not random noise and it is not hand-placed pixels. It is the signature of ordered dithering: a tiny repeating grid of threshold values that tricks your eye into seeing colors that are not actually there.
The most famous of these grids, the Bayer matrix, was published in 1973 by B. E. Bayer in the paper “An optimum method for two-level rendition of continuous-tone pictures” (Proc. IEEE Int. Conf. Commun., pp. 26-11–26-15). More than fifty years later, the same matrix is still running inside shaders, pixel-art tools, and 60-frames-per-second web animations. Here is how it works, why it differs from error diffusion, and when you should use it on purpose.

Cover illustration: a sunset drawn by the algorithm itself — every dot in this image was decided by an 8×8 Bayer matrix. (Original diagram by the author.)
01 //The Banding Problem
Pixel artists and retro developers live inside small palettes: four shades of teal, sixteen colors total, one bit of depth. A smooth gradient has nowhere to go — it collapses into harsh bands, the visual fingerprint of simple rounding. The left sphere below is shaded with a four-color palette and no dithering: watch how the bands slice the surface into flat steps.
The right sphere uses the same four colors. The only difference is a Bayer matrix deciding, pixel by pixel, whether to step up to the next shade. The bands dissolve into a fine ordered texture — the classic retro shading look.
Same 4-color palette, two renderings. Left: nearest-palette quantization. Right: ordered dithering with an 8×8 Bayer matrix. (Author's own rendering of the algorithm.)
02 //Four Ways to Fake a Gradient
The image below renders the same smooth black-to-white ramp at one bit of depth four different ways:
- Simple threshold cuts at 50%: everything below is black, everything above is white. No illusion at all.
- Random dither compares each pixel against a random number. It suggests tone, but the grain dances and never settles.
- Ordered dither (Bayer 4×4) compares each pixel against a fixed, repeating matrix. The result is the unmistakable crosshatch pattern — stable, rhythmic, deliberate.
- Floyd–Steinberg pushes each pixel's rounding error to its neighbors (see our deep dive on error diffusion). Smoothest of the four, but the pattern smears edges and crawls when animated.

Four renderings of one gradient at 1-bit depth, generated by the author's own implementations of each algorithm. Only the ordered row shows the regular crosshatch.
03 //The Bayer Matrix, Up Close
A threshold map of size n×n holds n² values spread as evenly as possible between 0 and n²−1. For pixel (x, y) with brightness b in the range 0–1, the rule is a single comparison:
That is the entire algorithm — no memory, no error carried forward, no dependence on any other pixel. This is why it parallelizes trivially on a GPU and costs almost nothing.
Bayer matrices come in powers of two and are generated recursively: a 2×2 seed grows into a 4×4, which grows into an 8×8, and so on. The 2×2 seed is simply [[0, 2], [3, 1]]. Each recursion step interleaves the previous matrix so the thresholds stay maximally spread out — Bayer's “dispersed dot” arrangement. Here is the full 8×8 map (values 0–63), exactly as defined by the recursion on Wikipedia's Ordered dithering page:

The 8×8 Bayer threshold map: 64 ordered thresholds. Compare any pixel's brightness against the cell it lands on. (Author's visualization of the standard recursion.)
A larger matrix yields finer steps — 64 thresholds means 65 distinguishable tones at 1-bit depth — but it also stretches the pattern across more pixels, so most pixel artists settle on 4×4 or 8×8 as the sweet spot. And the formula itself can collapse to pure bit-twiddling: on the Wikipedia page you will find the map expressed as M(i,j) = bit_reverse(bit_interleave(i XOR j, i)) / n², which is why 8×8 Bayer is a few ALU instructions in a fragment shader.
04 //Why Pixel Artists Still Reach for It
Image engineer Cris Luengo puts the practical case in one sentence: this method is “most commonly used in computer displays with limited palettes, because it is simple, fast, and deterministic (meaning it can be used in animations).” Determinism is the killer feature. Error diffusion recomputes every pixel from its neighbors, so a static gradient shimmers and crawls when the image moves. A Bayer pattern is frozen in screen space: frame two looks exactly like frame one. An indie developer even shipped a Bayer-dithered mouse trail running at 60fps in React, which would be impractical with error diffusion.
The second reason is aesthetic, and it is a feature rather than a flaw. As the Retro Dither Studio notes, ordered dithering “is fast and parallelises easily, which is why hardware used it” — and its visible crosshatch is “the texture of early PC games, GIF-era web graphics, and screen-printed posters.” When you want the pattern to be seen as a graphic device rather than hidden as texture, ordered dithering is the right tool. Gradient fades on a 16-color sprite, screen transitions in retro games, chunky sky gradients: these are all Bayer territory.
05 //Try It: 15 Lines of Ordered Dithering
Here is the whole thing in JavaScript, rendering a grayscale image to black and white with a 4×4 Bayer matrix:
const B4 = [
[ 0, 8, 2, 10],
[12, 4, 14, 6],
[ 3, 11, 1, 9],
[15, 7, 13, 5],
];
function orderedDither(gray, width, height) {
// gray: Float32Array of 0..1 brightness values
const out = new Uint8Array(width * height);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const threshold = B4[y % 4][x % 4] / 16;
out[y * width + x] = gray[y * width + x] > threshold ? 1 : 0;
}
}
return out;
}To shade between palette colors instead of black and white, scale the brightness into the gap between two neighboring palette entries and let the matrix decide which of the two wins — exactly the trick used for the sphere in section 01.
- Shading sprites or tiles on a tiny palette
- The image will move: walk cycles, scrolling, transitions
- You want the visible retro crosshatch as a style
- You are in a shader and every instruction counts
- Converting a still photo where smoothness matters most
- The pattern must stay invisible
- Edges need to survive dithering intact
- You can afford a serial, per-pixel pass
Three practical tips from people who dither photos for a living (via Retro Dither Studio): boost contrast before converting (flat images dither into mush); never downscale a dithered image in the browser or the pattern turns into moire — export at final size as PNG; and feed in a smaller source image first if you want the chunky, large-dot retro look.
06 //The Takeaway
Ordered dithering is the rarest kind of old idea: one whose limitations became its style. Bayer's 1973 matrix was engineered to squeeze smooth tones out of crude two-level printers, and the same dispersed-dot pattern now gives retro games their shading, gives shaders their cheap gradients, and gives pixel artists a way to stretch four colors into forty — deterministically, in parallel, in fifteen lines of code.
Next time you open PixKit's photo-to-pixel-art converter or any pixel-art tool with a “dither” toggle, you will know the tiny matrix making the decision under every pixel — and whether you want it to show its work.
07 //Sources
- B. E. Bayer, “An optimum method for two-level rendition of continuous-tone pictures,” Proc. IEEE Int. Conf. Commun., Conf. Rec., pp. 26-11–26-15, 1973 — citation via Ulichney's halftoning review and Purdue ECE 637 lecture notes.
- Bayer matrices and the recursion formula: Wikipedia — Ordered dithering (accessed 2026-10-20).
- Ordered vs. error diffusion trade-offs: Wikipedia — Dither; Cris Luengo's image analysis blog.
- When to use Bayer as a visible graphic device and practical conversion tips: Retro Dither Studio guide.
- Bayer dithering at 60fps in a modern web app: Botnari Creative — dithered mouse trail.
- Ordered dithering with arbitrary palettes and alternative matrices: matejlou.blog.