Photo Editing

Dithering Side by Side: Why Atkinson Looks Like a Mac and Bayer Like a Game

Eight dithering methods on one picture, with the same palette and the same options. Every kernel and divisor written out, the Bayer matrix built by recursion, and the linear light step that almost every quick implementation skips.

Dithering Side by Side: Why Atkinson Looks Like a Mac and Bayer Like a Game

Send a photograph to a thermal receipt printer and you meet the problem in its purest form. The ESC/POS raster image command takes one bit per dot: a bit set to 1 is printed, a bit set to 0 is not, and the row width is counted in whole bytes. There is no grey in that data stream. Something upstream has to decide, for every single dot, black or nothing, and the quality of that decision is the whole picture.

The same shape of problem is everywhere once you look. Colour e-paper does not mix inks: E Ink describes its ACeP platform as a four particle system that reaches eight primary colours, so your photo has to be rewritten in a handful of fixed inks before the panel will show it. A laser engraver either fires at a position or it does not. A screen print has one ink per screen. A Game Boy screen was two bits per pixel, four shades, and nothing in between.

Dithering is how you cheat that limit: scatter the available colours so the eye averages them back into the tone you wanted. Every method does that, and every method leaves a signature you can recognise across a room. The Floyd-Steinberg vs Atkinson comparison is the classic one, because Atkinson throws away a quarter of its error on purpose and the result has the hard, sooty contrast that people read as “old Macintosh” without knowing why.

Eight methods, one picture

Comparisons of dithering algorithms are usually dishonest by accident: different pictures, different palettes, different scaling, screenshots from different decades. The lab below runs all eight methods on the picture you give it, with the same palette, the same options and the same output size, so the only variable left is the algorithm.

Dithering Lab

Knock a picture down to a handful of colours and watch the method decide everything. The same picture, the same palette, eight algorithms side by side, with a loupe to compare two of them on the same pixels. The picture is read and drawn in this browser tab, nothing is uploaded and nothing leaves the page.

Dithered Floyd-Steinberg

Click anywhere on this picture to move the loupe below.

Original

Drawing the sample picture.

Drop a picture here
or press Enter to pick one. It is read on your own machine and never sent anywhere.
The method

Every tile is the picture you loaded, dithered with the palette and the options below, so the comparison is honest. The tiles are small on purpose: a dither that only looks good at full size is not a dither, it is a texture.

Palette and options

Serpentine turns every second row around, which breaks up the diagonal streaks a one-way scan leaves behind. Linear light is the one almost every implementation gets wrong: an sRGB byte is not a quantity of light, so adding an error to it darkens or lightens the wrong amount, and large dark areas come out visibly too pale. Both switches only touch the error diffusion methods, because the ordered ones have no error to carry.

Loupe, the same pixels twice
Floyd-Steinberg
Atkinson

Both halves show the same rectangle of the same picture, so the only difference you see is the method.

Where this still earns its keep, and where it does not

It earns its keep wherever the output really has few inks: e-ink panels, thermal receipt printers, laser engraving, screen printing, badge and label printers, and indexed PNG or GIF where a gradient would otherwise band. It does not earn its keep on a photograph headed for a normal screen, where twenty-four bit colour needs no help, and it actively hurts in front of a lossy encoder: JPEG, WebP and AVIF spend their whole bit budget describing the noise you just added, so a dithered source is both bigger and worse. It also does not survive resampling. Dither last, at the size and for the device that will actually show it.

Your picture is read with FileReader and drawn on a canvas in your own tab. There is no upload, no fetch, no external script or font: the file never leaves the browser, which matters when the thing you are testing is a client logo or an unreleased product shot.

One loop, five different tables

Error diffusion is a single idea. Walk the pixels in order. For each one, pick the nearest colour the output can actually produce, then measure what you got wrong: the difference between the value you wanted and the value you emitted. Push that error into the neighbours you have not visited yet, in fixed proportions. A pixel that came out too dark makes the next few pixels a little brighter, and the average survives even though no individual pixel is right.

Everything that distinguishes one error diffusion method from another lives in the table of proportions. X marks the pixel being processed, the numbers are the shares, and the divisor underneath is what you divide by.

Floyd-Steinberg            / 16      sum = 16
        X   7
    3   5   1

Jarvis, Judice, Ninke      / 48      sum = 48
        X   7   5
3   5   7   5   3
1   3   5   3   1

Stucki                     / 42      sum = 42
        X   8   4
2   4   8   4   2
1   2   4   2   1

Sierra Lite                /  4      sum = 4
        X   2
1   1

Atkinson                   /  8      sum = 6
        X   1   1
1   1   1
    1

Read those tables and you can predict the output. Floyd-Steinberg spreads over four neighbours and one extra row, which is cheap and keeps detail tight, but the error travels mostly rightward and downward and can pull into faint diagonal worms in flat areas. Jarvis, Judice and Ninke spread the error over twelve cells and two further rows, so the grain looks softer and more photographic, at roughly three times the work per pixel. Stucki uses the same footprint with weights that are all powers of two over a divisor of 42, a speed decision from the era of integer arithmetic, and comes out slightly crisper than Jarvis. Sierra Lite is three cells over a divisor of 4, the smallest table that still behaves.

Table comparing five error diffusion kernels by divisor, cell count, rows and how much error they keep, with Atkinson highlighted at 75 per cent, above a four step flow of the per pixel loop: read pixel, pick nearest palette colour, measure error, push shares to unvisited neighbours.

Atkinson keeps only three quarters of the error

Look at the Atkinson table again. Six cells, each worth one eighth, against a divisor of eight. The weights add up to six, not eight, so two eighths of every error are simply dropped on the floor. That is not a typo in the table and not a bug in the implementation. Bill Atkinson wrote it at Apple for the original Macintosh, and only three quarters of the error is diffused outward on purpose.

The consequence is easy to reason about. Error diffusion normally conserves tone: whatever a pixel loses, its neighbours owe back, so a flat area comes out at the right average brightness however far it is from a palette colour. Atkinson breaks that contract. Near white, the small debts that would have accumulated into an occasional black dot keep evaporating, so the region goes clean white; near black it goes solid black. The midtones, where the errors are large, still dither normally. Local detail with high contrast and crushed ends is the look people recognise from a 1984 screen, and it is why Atkinson is the right pick for a logo headed for one-bit output and the wrong pick for a photograph where the sky has to hold its tone.

Ordered dithering moves the threshold, not the error

Bayer and blue noise work on a completely different principle. Nothing is carried anywhere. Each pixel is compared against a threshold that depends only on its position, read out of a small tile that repeats across the image. Because no pixel depends on any other, ordered dithering parallelises perfectly, survives being computed on tiles, and produces no jitter between frames of an animation, which is why it is still used where error diffusion cannot be.

The Bayer matrix is not a magic table to be copied off a forum. It is built by recursion from a single zero, each step quadrupling the previous matrix and adding a fixed offset per quadrant.

M(1) = [ 0 ]

           [ 4*M(n)      4*M(n) + 2 ]
M(2n)  =   [ 4*M(n) + 3  4*M(n) + 1 ]

M(2) =  0  2      M(4) =   0  8  2 10
        3  1              12  4 14  6
                           3 11  1  9
                          15  7 13  5

threshold(x, y) = ( M(x mod n, y mod n) + 0.5 ) / n^2

The offsets 0, 2, 3, 1 are what makes it work: each quadrant is filled in an order maximally out of step with its neighbours, so at every scale the dots that turn on sit as far apart as the grid allows. The + 0.5 in the normalisation matters more than it looks. Without it the smallest threshold is exactly 0, one pixel in every tile flips a step too early, and flat tones pick up a visible bias. Tile sizes of 2, 4, 8 and 16 differ in scale rather than subtlety: a 2 by 2 tile is a coarse checker, a 16 by 16 tile a fine plaid that reads almost as texture.

The cost is the crosshatch. Bayer’s regularity is exactly what the eye is best at detecting, and in a gradient it shows as a grid laid over the picture. That is the “looks like a game” signature: consoles and early PCs used ordered dithering because a fixed matrix costs no memory and no second pass, and the pattern became the period’s accent.

Blue noise, computed the same way every time

Blue noise is the answer to Bayer’s grid. The idea is to keep the position dependent threshold, but fill the tile so that the pattern has no repeating structure at any scale while still keeping every dot as far from its neighbours as possible: energy concentrated at high spatial frequencies, almost none at low ones. Your visual system is poor at high frequencies, so the pattern disappears into texture instead of announcing itself as a grid.

The lab builds its 32 by 32 tile with void-and-cluster, the method Robert Ulichney published in 1993: find the tightest cluster of dots, remove one, find the largest void, put it there, repeat until the arrangement is stable, then rank every position by that process. The starting pattern is an R2 low discrepancy sequence rather than a random seed, so no random number enters the construction. The matrix is byte identical on every machine and after every reload, which is what you want when a client asks why yesterday’s export looks different.

Serpentine scanning

Error diffusion has a direction, and direction leaves tracks. Scanning every row left to right means every error is pushed the same way, and in areas of slowly changing tone the residue lines up into diagonal streaks that all lean the same direction. Serpentine scanning mirrors the horizontal offsets on every second row, so odd rows push their error left and even rows push it right. The bias cancels row by row instead of accumulating down the frame.

It costs one sign flip in the inner loop and it is the cheapest quality improvement in the subject. It does nothing at all for Bayer or blue noise, because there is no error being carried, which is why that switch goes visibly dead when you select an ordered method rather than silently pretending to work.

The error belongs in linear light

Here is the mistake almost every quick implementation makes, including a lot of published ones. The byte in your image buffer is not an amount of light. It is an sRGB encoded value, and the encoding is deliberately non linear so that the 256 available steps are spread evenly across what the eye can distinguish rather than evenly across physical intensity.

c = byte / 255

L = c / 12.92                        when c <= 0.04045
L = ( (c + 0.055) / 1.055 ) ^ 2.4    when c >  0.04045

Run byte 128 through it. As a fraction that is 0.502, halfway up the scale, but the light it stands for is 0.216, just under 22 per cent. Meanwhile an even checkerboard of black and white pixels emits exactly 50 per cent of full light, because dots average in light, not in bytes. So a dithering algorithm that averages a checkerboard against byte 128 is comparing two quantities that are not the same kind of number, and the whole error term inherits the mistake.

The visible result is dark areas coming out too pale and the tone curve of the whole picture bending. The fix is to decode to linear light, do the quantisation and the error arithmetic there, and encode back on the way out. Switch it on and off on a photograph with a large shadow area and the difference is not subtle. This is the same family of problem as images that come out washed out after WordPress processes them: an assumption about what the numbers in a pixel mean, made once and then carried through everything downstream.

The sRGB to linear light formula with its threshold of 0.04045, divisor 12.92, offset 0.055, scale 1.055 and exponent 2.4, above a table showing that byte 128 stands for 21.6 per cent of full light while byte 188 stands for 50.3 per cent.

Ordered dithering is the exception, and the lab treats it as one. The Bayer and blue noise thresholds perturb in sRGB, not in linear light, because the threshold offset is a perceptual step rather than a quantity of light. Its amplitude comes from the mean nearest neighbour distance in the palette, which gives the right spread for two levels, for n grey steps and for the web safe grid alike. The tests assert that Bayer output is byte identical with both switches flipped.

The palette decides more than the method

People argue about algorithms and then hand them an unsuitable palette. The available colours set the ceiling; the method only decides how gracefully you approach it. The lab offers black and white, n grey steps from 2 to 64, the web safe 216 (six values per channel: 00, 33, 66, 99, CC, FF, a 1990s artefact of 256 colour displays), the 16 colour CGA and EGA set built from three RGB bits plus an intensity bit, four shade Game Boy, a seven ink e-paper set, and median cut.

Median cut is Paul Heckbert’s 1982 algorithm: put every pixel in one box, repeatedly split the box with the greatest range along its longest axis at the median, and average each final box to get a colour. It adapts to the picture, which is why a sunset in sixteen median cut colours beats the same sunset in the sixteen colours of CGA. It stops early when no box can be split, so a two colour picture gives back two colours however many you asked for, and the status line reports how many palette entries were actually used rather than how many were offered. If you are picking a fixed set by hand instead, the reasoning in how to choose a colour palette without learning colour theory applies here too.

Finding the nearest palette colour for every pixel is the inner loop, and a linear scan over the palette for each pixel is the naive version. The lab buckets the RGB cube into 16 by 16 by 16 cells and keeps, per cell, only the palette entries that can still win somewhere inside it. That is exact rather than approximate, and the tests verify it against a full scan.

What the lab deliberately will not do

The preview and every thumbnail are computed at the exact pixel width of the box they are shown in, capped at 640, because a dithered image rescaled by the browser shows moire that is not in the picture and you would be comparing scaling artefacts instead of algorithms. There is no resize listener, so those sizes hold until you load a new picture. The full size export is capped at 1600 pixels wide. The 1-bit PNG re-runs the current method against a black and white palette rather than thresholding the coloured result, because thresholding a dithered colour image gives noise, not a bilevel version of it. Two instances on one page are not supported, the same limit the rest of the plugin has.

Two more limits are about your workflow rather than the tool. Dithering does not belong in front of a lossy encoder: a dithered image is maximum spatial frequency by construction, which is precisely what a JPEG or WebP quantiser discards first, so you get mush and a larger file at once. The tradeoffs in choosing between WebP, AVIF, JPEG and PNG and in what actually costs the bytes in an image both point at PNG or a raw bit buffer. And dithering does not survive resampling: scale a dithered image and the pattern beats against the new pixel grid. Dither last, at final size.

If the destination is a press rather than a panel, the printer’s own halftone screen is already doing something like this, and dithering on top of it fights the RIP. The differences between screen and press are covered in what actually changes when a design gets printed. Dither for devices that take your bits literally: receipt printers, e-paper, engravers, LED matrices.

Nothing here is exclusive. ImageMagick has had these kernels for decades, every graphics library ships Floyd-Steinberg, and there are other browser based dither tools. What the lab is for is the comparison: the same picture, the same palette, the same options, eight methods at once, with the numbers that produce each result written down rather than hidden behind a preset name.

Once the tables are in front of you the choice stops being a matter of taste. Line art and logos to one bit: Atkinson, for the contrast its missing quarter buys. Photographs where tone has to hold: Jarvis or Stucki, in linear light, serpentine on. Anything animated, tiled or computed in parallel: blue noise, because it has no memory and no seam. Bayer when you want the grid to show, which is a design decision and not an accident.

The reason to understand the mechanism rather than cycle through presets is that the failures become diagnosable. Diagonal worms in a flat sky mean directional bias, so turn serpentine on. Chalky shadows mean the error is being diffused in sRGB bytes. A gradient that bands in steps rather than dissolving means the palette is too small for the tonal range, not that the algorithm is wrong. Naming the cause is faster than trying all eight again.

WunderPaint exists for the ordinary image work inside WordPress, the cropping and resizing and library tidying that fills a normal week. This lab sits at the other end of the scale, for the day something has to come out of a device that only understands on and off. Load a picture, put the eight tiles side by side, and let the picture settle the argument.

Dithering Side by Side: Why Atkinson Looks Like a Mac and Bayer Like a Game

Table of Contents

Learn it by building something

Every week one thing you can make the same afternoon, from dynamic templates to 3D type. Written down step by step.

One mail a week, and then it ends.
Unsubscribe in one click.

AI Images

AI Product Photos That Do Not Look Fake: Light, Shadow, Perspective

A real product photo on a generated background gives itself away on exactly three axes: light direction, shadow quality and perspective. Here is the physics in plain words, a browser tool that walks you through all three checks on your own composite, and the prompt lines that stop the mismatch happening in the first place.

WordPress Development

Dynamic Templates: Build the Design Once, Let Your Posts Fill It In

You build the layout once, tell a few layers where to get their content, and from then on every post, product or page renders its own version.

Photo Editing

Content Aware Resize: Changing a Photo’s Shape Without Squashing Anyone

One photograph has to be a wide header, a square thumbnail and a tall story card. Cropping loses the edges, stretching lies about proportions, letterboxing buys space it never uses. Seam carving spends the quiet parts of the frame instead: what a seam is, why the search needs a table, and exactly where the method falls apart.

Design Fundamentals

Ten Typography Mistakes That Give an Amateur Design Away

Bad type is rarely the wrong font. It is almost always spacing, size and a handful of habits nobody ever told you to drop.

Speed & Performance

Regenerate Thumbnails in WordPress: What It Fixes and What It Leaves Behind

Regenerating thumbnails re-encodes every registered size from the original file. Here is when it is genuinely needed, what each WP-CLI flag really does, why --only-missing quietly keeps the old files, and the order that stops your uploads folder growing.

WordPress Images

WordPress Image Sizes Explained: Every File Created From a Single Upload

One upload becomes eight or more files. Here is what WordPress actually creates, why some sizes are missing, and what to do about it, with numbers measured on a real site.

Download the free WunderPaint Plugin for WordPress

The WunderPaint workspace with the layers panel, adjustment sliders, text style presets and the asset library along the bottom

The Image Editor & Design Studio

Everything described here can be done in the browser, on your own site. The live demo runs the full editor with nothing to install.

Free

Chaos Art

Autonomous painters make one-of-a-kind abstract art in 3D space - gestures, art movements, painterly media, and embeds that paint a new original for every visitor.

Pro

Particle Strokes

Paint with swarms of light: twenty-two movements, a stamp you draw yourself, and curves that give a stroke a shape - the swarm keeps painting for a few seconds after you let go.

Pro

City Diorama

Any place on earth as a miniature you could hold: real streets, water and building footprints raised into a 3D diorama - or wrapped around a sphere as your own tiny planet.

Free

Papercut Art

Layered paper pictures with real depth - a photo sliced along its actual depth into up to twenty layers, parametric landscapes, animals and clouds you can shape, letters with real counters, and a look that runs on three dials.

Pro

3D Earth Studio

A hyperrealistic globe - day and night with city lights, live clouds, atmosphere halo, country borders and highlights, click-to-place markers with flight-route arcs, satellite orbits, seamless rotation video and a live website embed.

Free

Mystic Studio

Turn a birth date into wall art - a real natal chart with houses and aspects, the moon of that night, zodiac and Chinese zodiac posters, numerology cards and a synastry wheel for two, in eight artful themes.

Free

Marble Bath

Marble paper on a virtual water bath - drop, rake and comb real Ebru patterns with gestures, flowers and classic recipes, razor-sharp at any size and re-editable as a layer.

Free

Day Ring

Turn a day into a beautiful circular schedule - colour-coded time blocks as arcs around a 24-hour clock, with concentric rings for overlaps, emoji, templates and a legend.

Free

Code Shot

Turn code into a gorgeous, share-ready image - syntax highlighting, editor themes, window frames and diff highlighting - then drop it into your design as a re-editable layer.

Pro

3D Solar System Studio

Build a date-accurate 3D solar system - real planet positions for any date, photoreal textures and one slider from artistic to true scale - then drop it into your design as an editable layer.

Pro

3D Molecule Studio

Build a real 3D molecule - from a curated library, the periodic table or a SMILES string - then style it, measure it and drop it into your design as an editable layer.

Pro

3D Textile Studio

Drop your design onto cloth that behaves like the material you pick: silk falls soft, felt holds its shape, flag fabric snaps in the wind. Hang it, blow it and drape it, then lay the finished drape back into your document as a picture.

Pro

3D Particle Studio

Point the engine at any layer and it becomes a cloud of particles that keeps its colours, flowing through a sphere, a galaxy or your own outline. Keep the frame you like as a still, or embed the running engine so it keeps moving on your page.

Free

Origami

Put your own picture on the paper and watch that very sheet fold itself into a crane or a box. Every step is a station you can stop at and turn around in 3D, which is exactly where printed diagrams leave you alone.