QR Codes That Actually Scan: Error Correction, Contrast and Print Size
The QR code that nobody could scan
A restaurant prints its new menu QR code on the corner of a laminated card. It scans perfectly on the designer's monitor. On the printed card, in the restaurant's warm evening lighting, roughly one customer in three gives up and asks for a paper menu.
This happens constantly, and it is almost never a problem with the QR standard. QR codes are remarkably robust — they were designed in 1994 to be read off dirty, scratched parts in a Toyota factory. When one fails to scan, it is nearly always because of a decision made when it was generated or placed: too much data, too little contrast, too small a print, or no quiet zone.
All four are avoidable once you know what the code is actually doing.
How a QR code survives damage
The thing that makes QR codes work in the real world is Reed–Solomon error correction. Every code carries redundant data alongside the payload, so a scanner can reconstruct the message even when part of the pattern is unreadable.
The standard defines four levels, and choosing between them is the most consequential decision you will make:
- L (Low) — recovers from about 7% damage. Smallest code for a given payload.
- M (Medium) — about 15%. The usual default.
- Q (Quartile) — about 25%.
- H (High) — about 30%. Largest code, most forgiving.
Higher correction is not free. The redundant data occupies modules — the little black and white squares — so at a fixed payload, level H produces a visually denser code than level L. Denser means each module is physically smaller at the same print size, and small modules are harder for a camera to resolve.
There is a real trade-off here, and it points in opposite directions depending on where the code will live:
- On a screen, scanned from close range: level M is fine. Nothing will damage a screen, and the pixels are crisp.
- Printed on paper, packaging, or a card: level Q. Print gets scuffed, folded, and thumbed.
- Outdoors, on vinyl, or with a logo overlaid: level H. Weather, glare, and any deliberate obstruction eat into your margin.
That last point is worth stating plainly, because it is the most common way people break their own codes: the logo you place in the middle is damage. It works only because the error correction is silently repairing it. If you overlay a logo on a level-L code, you may have already consumed your entire 7% margin before the code leaves the printer — and then one scuff finishes it.
Less data means a code that actually scans
The number of modules in a QR code grows with the amount of data encoded. This is the version, from 1 (21×21 modules) up to 40 (177×177 modules). A version 40 code at level H contains thousands of tiny squares. Printed at 2cm, those squares are smaller than the resolution of a phone camera at arm's length, and the code becomes unscannable no matter how good the error correction is.
So the single most effective thing you can do for scan reliability is encode less.
Compare:
https://example.com/menu
→ 24 characters, version 2, tiny and sharp
https://example.com/restaurant/locations/downtown/menu/summer-2026?utm_source=table_card&utm_medium=qr&utm_campaign=summer_launch
→ 130 characters, version 6+, visibly denser
Both go to the same place. The second is dramatically harder to scan for the sake of tracking parameters that could be handled with a short redirect URL instead.
Practical ways to shrink the payload:
- Use a short URL or a dedicated short path.
example.com/mthat redirects is far better than a long canonical URL. - Drop
https://www.where you can. Most scanners handleexample.com/mcorrectly. That is twelve characters saved. - Use uppercase for alphanumeric payloads. QR has a dedicated alphanumeric mode that packs uppercase letters, digits, and a few symbols more efficiently than byte mode. Lowercase letters force byte mode. For a code like
ABC-1234, uppercase is genuinely smaller. - Move tracking to the server. Redirect through a URL you control and log the hit there, rather than stuffing UTM parameters into the code.
Contrast, and the mistake of inverting
Scanners find a QR code by looking for the three large squares in the corners — the finder patterns — and they expect dark modules on a light background.
Some decoders handle the inverse, light-on-dark. Many do not, and the ones that do are often slower about it. If a code has to work for the general public with whatever phone they happen to own, do not invert it.
Colour has the same constraint, which people discover the hard way when a brand-coloured code fails in dim light. What matters is not the hue but the luminance difference between foreground and background. A medium blue on a medium grey may look like plenty of contrast to your eye and read as nearly identical brightness to a camera sensor. Rules that hold up:
- Keep the dark modules genuinely dark. If you must brand the code, brand it with a deep, saturated colour, not a mid-tone.
- Keep the background genuinely light. White is best. Off-white is fine. A photograph behind the code is not.
- Never place a code on a gradient, a texture, or an image. The scanner has to decide a single threshold between light and dark, and a varying background makes that impossible in part of the frame.
- Avoid yellow and other light colours for the modules themselves, even on white — the luminance gap is too small.
The quiet zone is not optional
The QR specification requires a margin of blank space around the code, four modules wide, called the quiet zone. Its job is to tell the scanner where the code ends.
Designers delete it constantly, because it looks like wasted space. Crop a code flush to its edge and place it against a dark background, and a scanner may fail to locate the finder patterns at all — the code and the background merge into one shape.
Keep the white margin. If your layout is tight, shrink the code and keep the margin rather than the other way round; a smaller code with a proper quiet zone scans better than a larger one without.
How big to print it
There is a well-known rule of thumb that works well in practice: the code should be at least one tenth of the distance it will be scanned from.
At a 30cm reading distance — a table card, a business card, a product label — that means 3cm minimum. Bigger is safer.
- Business card, label, table card: 2.5–3cm minimum, more if the payload is long.
- A4 poster read from about 1m: 10cm.
- Shop window or wall read from 3m: 30cm.
- Billboard read from 20m: 2m. And keep the payload extremely short.
Two additional print considerations: generate the code at high resolution — at least 300 DPI at final size, and ideally as SVG, so the module edges stay perfectly sharp rather than blurring under interpolation. And leave it matte if you can. Glossy lamination under a ceiling light produces a bright specular reflection that wipes out an entire region of the code, which is exactly the restaurant menu problem described at the start of this article.
Beyond URLs: the formats worth knowing
A QR code can hold structured data that phones recognise and act on, which is often more useful than a link:
- Wi-Fi credentials — the guest scans and joins, without you reading a 20-character password aloud. Note the payload contains the password in plain text: anyone who photographs the code has your Wi-Fi key. Fine for a guest network, not for your main one.
- vCard contact details — scanning adds you to their contacts directly. Keep it to name, one phone number, one email, and a website; a full vCard with address and photo balloons the payload and produces a dense, fragile code.
- Plain text — serial numbers, asset tags, short instructions.
- Email or SMS — pre-filled recipient and subject, useful for support and feedback flows.
Our QR Code Suite generates all of these, lets you set the error correction level explicitly rather than hiding it behind a default, and exports at print resolution. Because it runs entirely in your browser, Wi-Fi passwords and contact details are encoded on your own machine and never sent anywhere.
Test it the way your users will
The final step takes two minutes and catches nearly everything:
- Print it at final size, on the actual material, from the actual printer.
- Scan it with a cheap old phone, not your newest one.
- Scan it in the lighting the code will live in — dim restaurant, bright shop window, outdoors at an angle.
- Scan it from realistic distance and from about 30° off-centre.
- Scan it with the native camera app, not a dedicated QR app. That is what people actually use.
If it reads instantly in all of those, it will hold up. If it takes a couple of seconds anywhere, go up one error-correction level or one size step — the margin you add now is what absorbs the wear the code will pick up later.
A note on permanence
One last thing, because it causes more grief than any technical detail: a QR code is a printed, immutable object pointing at a mutable destination.
If you print ten thousand cards pointing at example.com/menu/summer-2026 and then restructure your URLs, every one of those cards is now a dead end. Point printed codes at a short, stable path that you control and can redirect at will. The code is permanent. Design the destination so it does not have to be.