technique
Dithering
Mix two neighboring ramp colors in a fixed pattern so a large area reads as a smooth gradient without adding a single color to the palette.
Before this
This page assumes you are comfortable with:
Why you need this
A ramp has four colors, so a sky, a metal sheen, or a big rounded shape shaded with it shows four hard bands. Adding more colors to the ramp fights the whole point of a small palette. Dithering solves the problem inside the palette: it interleaves two adjacent ramp colors in a pattern fine enough that the eye averages them into a color between. It belongs to stage 2 of the pipeline, "draw the still", and it is the technique most often overused by people who have just learned it, so this page spends as much time on where not to use it as on how.
The idea
Dithering is placing pixels of two colors in a repeating pattern over an area so that, at viewing distance, the area appears to be a single intermediate color. The two colors must be adjacent steps of the same ramp (see Palettes and ramps), because the eye can only average colors that are already close; a checkerboard of the darkest and lightest step reads as a checkerboard.
The problem, on a strip of four colors A (darkest) through D (lightest), with no dithering:
A A A A B B B B C C C C D D D D
A A A A B B B B C C C C D D D D
A A A A B B B B C C C C D D D D
A A A A B B B B C C C C D D D D
Four flat bands with three hard edges. This is called posterizing the gradient.
Checkerboard (50/50). The simplest dither alternates the two colors on every pixel in both directions. Half the pixels are A, half are B, and the patch reads as a color halfway between:
A B A B
B A B A
A B A B
B A B A
Partial patterns. For a color one quarter of the way from A to B, light one pixel in four. For three quarters, light three in four:
B A B A B B B B
A A A A A B A B
B A B A B B B B
A A A A A B A B
Left: 25 percent B. Right: 75 percent B. Together with the flat colors and the checkerboard, a four-color ramp now has thirteen apparent levels: four flat, three at 50 percent between neighbors, and six at 25 or 75 percent.
Ordered dithering. Rather than choosing patterns by hand, a threshold matrix decides each pixel. The 4 by 4 Bayer matrix is:
0 8 2 10
12 4 14 6
3 11 1 9
15 7 13 5
Every value from 0 to 15 appears once, arranged so that any set of the lowest values is spread as evenly as possible over the 4 by 4 block. The rule: for a pixel at whose desired color lies a fraction of the way from A to B, look up the matrix cell at and give the pixel the lighter color B if exceeds that cell's value, otherwise A. At the cells with values 0, 1, 2, 3 light up, which is exactly the 25 percent pattern above. At the values 0 through 7 light up, which is the checkerboard. The matrix contains every hand pattern you would have chosen, plus the twelve in between.
Random dithering replaces the matrix with a random number per pixel: B if exceeds a uniform draw in . On average it gets the same mix, but the pixels clump and leave gaps, so the area reads as noise or static rather than a smooth tone. It has one use, dirty or granular textures, and even there a seeded generator so the noise does not change between builds.
Worked example
A strip 4 pixels wide and 16 tall, where the desired color runs from pure A at the top row to almost pure B at the bottom: row has . Each row uses matrix row . The first rows, worked by hand:
| Row | Matrix row | Compare against each cell | Result | |
|---|---|---|---|---|
| 0 | 0 | 0 8 2 10 |
0 exceeds none | A A A A |
| 2 | 2 | 3 11 1 9 |
exceeds only the 1 | A A B A |
| 4 | 4 | 0 8 2 10 |
exceeds 0 and 2 | B A B A |
| 5 | 5 | 12 4 14 6 |
exceeds only the 4 | A B A A |
| 9 | 9 | 12 4 14 6 |
exceeds 4 and 6 | A B A B |
| 12 | 12 | 0 8 2 10 |
exceeds all four | B B B B |
| 15 | 15 | 15 7 13 5 |
exceeds 7, 13, 5 but not 15 | A B B B |
The complete strip:
A A A A
A A A A
A A B A
A A A A
B A B A
A B A A
B A B A
A A A B
B A B A
A B A B
B A B B
A B A B
B B B B
B B A B
B B B B
A B B B
A darker ramp step, B the next lighter step. Count the B pixels per row: 0, 0, 1, 0, 2, 1, 2, 1, 2, 2, 3, 2, 4, 3, 4, 3. Any 4 by 4 block averages to the right level, even though single rows wobble, which is why the pattern is judged over blocks and why a strip narrower than 4 pixels cannot dither properly.
Here is a generator that renders a horizontal gradient through a whole ramp with Bayer dithering, at an integer display scale. Paste it into a browser console:
const BAYER = [[0, 8, 2, 10], [12, 4, 14, 6], [3, 11, 1, 9], [15, 7, 13, 5]];
function ditherGradient(width, height, ramp, scale = 3) {
const canvas = document.createElement("canvas");
canvas.width = width * scale; canvas.height = height * scale;
const ctx = canvas.getContext("2d");
const gaps = ramp.length - 1; // pairs of adjacent ramp colors
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const pos = (x / width) * gaps; // 0 .. gaps, position along the ramp
const i = Math.min(Math.floor(pos), gaps - 1); // index of the darker color of the pair
const g = pos - i; // 0 .. 1 between ramp[i] and ramp[i + 1]
const lighter = g * 16 > BAYER[y % 4][x % 4];
ctx.fillStyle = lighter ? ramp[i + 1] : ramp[i];
ctx.fillRect(x * scale, y * scale, scale, scale); // one art pixel = scale x scale screen pixels
}
}
document.body.appendChild(canvas);
}
ditherGradient(64, 16, ["#39192a", "#7c252f", "#bf4b39", "#e6b285"]);
Change the ramp to ["#2a2f4a", "#2f6b3a", "#6bb04a", "#c8de6e"] for the grass ramp, or shorten it to two colors to see a single dithered transition.
Where dithering belongs
Large areas with slow gradients: a sky from horizon to zenith, a distant hill fading into haze, the broad side of a big rounded shape, the sheen down a metal door. In each case the area is at least 8 pixels across, the two colors are ramp neighbors, and the eye is not being asked to read detail there.
Dithering also works as texture, on purpose. A 50/50 checkerboard of two stone tones reads as rough stone; a sparse 25 percent scatter of a dark tone on dirt reads as gravel. That is the effect the eye produces when a dither is too coarse to average, so use it where you want roughness and nowhere else.
Where it does not
Small sprites, where every pixel is already a decision. Faces, where a dithered cheek reads as stubble or dirt. Anything under about 3 pixels wide, where there is not room for the pattern to repeat and it reads as stray pixels. Across a form's outline, where it destroys the silhouette. Between two tones that are meant to be a hard edge, such as the faces of a cube; the edge is the information.
In a game's art pipeline
Dithering is a stage 2 tool that mostly lives in stage 4, "build the world": background layers in Backgrounds and parallax use it for skies and distant haze, and Tileable textures and tilesets uses it as texture on stone and dirt. On sprites it appears on the largest forms only, usually metal, after Shading and lighting has placed the flat tones. Stage 5 matters as much as the drawing: a dithered area survives only if the engine draws it at an integer scale with nearest-neighbor filtering, which Sprite sheets and export covers. Purchased packs often dither their skies, and Adapting purchased packs has to preserve the pattern when it recolors them.
Common mistakes
- Dithering everything. Symptom: every surface has a checkerboard on it and the whole screen looks like static; the flat, clean clusters that pixel art depends on are gone.
- Dithering across an outline. Symptom: the edge of a sprite dissolves into speckles and the silhouette stops reading at 1x.
- Non-adjacent ramp colors. A checkerboard of the shadow and the highlight. Symptom: the eye sees a checkerboard, not a midtone; the pattern reads as a tablecloth.
- Dithering under 3 pixels wide. A dithered strap or eyebrow. Symptom: random dots on the face, as if the sprite were dirty.
- Scaling that blurs the pattern. Bilinear filtering or a 2.5x scale in the engine. Symptom: the dither turns into a gray smear or a shimmering moire pattern as the camera moves; the art was fine and the export was not.
- Random dithering for a gradient. Symptom: the sky looks like sandpaper, and if the noise is unseeded it changes every time the game starts.
Cost
Ordered dithering costs almost no artist time once the area is chosen, because the pattern is mechanical and every editor has a dither brush; the time goes into deciding where the transition between two ramp steps falls, which is the same decision flat shading needs. It costs zero palette entries, which is the point. What it costs is legibility: every dithered pixel is a pixel that no longer carries a hard edge, so a dithered area cannot also hold detail. It also costs compression: a checkerboard is the worst case for run-length and PNG filters, so a large dithered sky is a bigger file than the same sky in flat bands, by a factor of two or three. It starts to hurt when dithering leaks from backgrounds onto sprites; a game where the hero's tunic is dithered has spent its legibility budget on a gradient nobody asked for.
Going further
- Anti-aliasing in pixel art, the other way of placing an intermediate color, one pixel at a time on edges.
- Backgrounds and parallax, for the skies and haze where dithering earns its place.
- Sprite sheets and export, for the integer-scale and filtering settings that keep a dither pattern intact.
- Raster images and pixels, for why a 2.5x scale cannot show a checkerboard correctly.