Halftone QR codes vs dithered QR codes
Both approaches put a picture inside a working QR code. They are not the same technique, and you can tell them apart across a room: one looks like a picture behind a screen door, the other like photographic grain. This article explains what each does, why they look different, where one clearly wins, and where neither does.
The halftone method
The classic halftone QR generator, in circulation since around 2013, triples the size of the code. Every module becomes a 3×3 block of sub-pixels. The centre sub-pixel is pinned to the module's required colour; the other eight are free to take whatever the (already thresholded) picture says. A scanner samples module centres, so the centres are what must be right — and they are. It works, and it produces a recognisable look: a regular grid of dots sitting on top of the picture, because every third pixel in both directions is pinned and those pinned pixels line up perfectly. Your eye is extremely good at finding a regular lattice. Once you see it, you cannot unsee it. The approach is simple to implement and easy to reason about, which is why so many generators use it, and for a code that will only ever be viewed on a screen at its native size it is entirely serviceable. Its limitations appear in print, where nothing is ever viewed at native size and a single pinned pixel per module has to survive the printer, the paper, the lighting and the camera's resampling on its own.
The dithered method
The dithered approach changes one thing and it changes everything. Instead of thresholding the picture first and then overriding pixels, it dithers the picture with error diffusion and treats a forced pixel as part of that process: force the value, measure the error the forcing caused, and push that error into the neighbouring pixels the way Floyd–Steinberg pushes any other error. The result is that the forced pixels are compensated for. The pixels around them shift to keep the local average right, so the forced dots sit inside the same irregular speckle as the rest of the image. There is no lattice for the eye to lock onto, and the picture reads as a picture rather than as a picture behind a screen door. The forced region also need not be a single pixel: it can be a block at the centre of each module whose size is a setting, which is what SWAPQR's Photo ↔ Code balance controls. The rest of the module — and the picture in it — is whatever the error diffusion leaves, which by construction has the right average tone for the module it belongs to.
Side by side
In the halftone method the picture is thresholded before the code is placed; in the dithered method the two happen together, in a single pass. Halftone forces one sub-pixel per module, on a perfect grid; dithering forces a centre block of adjustable size, hidden in the speckle. The visual signature of halftone is a regular dot lattice over the image; of dithering, photographic grain. Halftone offers little tuning — one of nine pixels is fixed and that is that — while dithering offers a continuous choice of how much of each module is forced. And in close-up robustness, halftone is weak, because one pixel per module survives no resampling, whereas the dithered result is tunable and, as the close-up article in this guide shows, measurable: below about half of the module forced, older block-thresholding readers struggle in extreme close-up; above it they do not. The two methods are not variations on a theme; they answer the question "where does the picture go" in different orders, and that order is what the eye sees.
Where dithering wins by a lot
Because the amount of forcing is a dial rather than a fixed one-in-nine, a dithered generator can trade picture against reliability on purpose. That is not a cosmetic difference: at one pixel forced per module, the code depends on a single pixel surviving every resampling step between the printer and the sensor, and often it does not. An inkjet spreads it, a laser printer rounds it, a laminate blurs it, a camera at an angle smears it across two sensor pixels, and the decoder's own downscaling averages it with its neighbours. Forcing a block instead — and letting error diffusion pay for it — is what makes the technique hold up in print. It also means the same generator can produce a nearly plain code for a sticker that people will hold a phone against, and a nearly pure photograph for a poster scanned from a metre away, from the same picture and the same link, by moving one control. Halftone has no such control: its reliability and its look are fixed by the method, and the only way to make it more robust is to abandon it for a bigger pinned region, at which point it has become the dithered method with worse compensation.
Where neither wins
Neither approach makes a bad photograph good, and neither survives a long payload. Both need error correction at its highest level, which itself makes the code denser. A flat, low-contrast image fails in both, for the same reason: it produces an even texture that competes with the modules. A long link fails in both, because the modules become too small to hold any picture. And both are, in the end, a deliberate trade: you are spending reliability to buy an image. Spend it where the image is worth something, and test the print. There is a third family worth naming: generators that place an untouched picture behind a normal code and simply lighten the modules, or that punch a logo out of the middle. Those are not encoding the picture at all — they are relying on error correction to absorb the damage. Perfectly valid, much less interesting, and much more limited in how much image you get, because the whole cost is paid from the error-correction budget with nothing compensating for it.
| Aspect | Halftone (3×3) | Dithered (error diffusion) |
|---|---|---|
| When the picture is thresholded | Before placing the code | Together with the code |
| Forced pixels | One per module, on a perfect grid | A centre block, hidden in the speckle |
| Visual signature | Regular dot lattice over the image | Photographic grain |
| Tuning available | Little — 1 of 9 pixels is fixed | Continuous — choose how much of each module is forced |
| Close-up robustness | Weak — one pixel per module survives no resampling | Tunable, and measurable |
Frequently asked questions
Which one does SWAPQR use?
The dithered method. The picture is reduced with Floyd–Steinberg error diffusion, the pixels the code needs are forced and their error is diffused into the neighbours, and the size of the forced block is the Photo ↔ Code balance.
Why does a halftone code look like it has a grid over it?
Because exactly one sub-pixel in every 3×3 block is pinned and those pinned pixels align perfectly in both directions. The eye finds a regular lattice immediately.
Is a logo cut out of the middle the same thing?
No. That relies on error correction to absorb a damaged region; nothing compensates for the damage. It is valid, but it allows far less image than either the halftone or the dithered method.
Do both need the highest error correction?
Yes. Both spend part of the code's recovery budget on the picture, so both are generated at the highest level, which makes the code denser and is one more reason to keep the link short.
Print the square once. Decide later where it goes. SWAPQR makes static QR codes for free, with every style option, no account and no watermark. A paid plan turns a code dynamic: the printed square stays the same while you change its destination, and you see how often it was scanned, by day and by device.
Make a QR code in the browser