
Choosing Paper for a QR Code: Coated, Uncoated and Recycled
The same PDF went to two printers. The version on silk-coated card reads from half a meter, first try, on every phone in the office. The version on 100 gsm uncoated letterhead needs two or three attempts and refuses past about twenty centimeters. Identical file, identical dimensions, identical black. What differs is what the paper did to the ink after it landed.
Paper decides how much every dark module grows
Ink is wet and paper is absorbent. Some of it stays where it was put and some spreads sideways into the fiber. Printers call the result dot gain, and it is a normal, expected, compensated part of every printing process. On a photograph it costs a little shadow detail. On a QR code it does something specific: every dark module grows slightly in all four directions, so every light gap between two dark modules narrows by twice that amount. The dark squares fatten and the light channels between them close.
That is the whole mechanism, and most of what follows is a consequence of it. A decoder samples the middle of each module position and asks whether it is dark or light. Modules that are a little fat are fine. Two neighboring dark modules that have merged into one rectangle are not, because the light module that was supposed to sit between them has stopped existing.
Uncoated stock drinks it
Uncoated paper is paper: cellulose fiber with nothing sealing the surface. Letterhead, copier paper, most notepads, the inside pages of a paperback. Ink sinks in and wicks along the fibers, which is why uncoated has the highest dot gain of any common surface. The spread is uneven too, because fiber runs in directions and wicking follows it, so on a small code some gaps close before others and the failure looks random rather than symmetrical.
Then there is the white. Uncoated stock is rarely bright white, and even the bright grades scatter light rather than reflecting it evenly, so against a coated sheet the light modules sit a step darker. The dark modules have not moved. Separation narrows from the light end while dot gain narrows it from the dark end, and separation is what a decoder is actually measuring. The thresholds that break contrast sit closer than a screen proof suggests.
None of that rules uncoated out. It means the code has to be bigger than it would be on coated stock, with a shorter payload so there are fewer modules to fit into that size.
Coated stock holds the edge, then creates a different problem
A coated sheet carries a mineral layer, usually clay or calcium carbonate, sitting on top of the fiber. Ink stays on that layer instead of sinking into it, so dot gain drops sharply and module edges land close to where the file put them. For a code that has to be small, coated is the easier surface.
Gloss coated is where it turns. The same smoothness that holds the ink edge also makes the sheet behave partly like a mirror, and a phone held at the reflection angle receives a bright highlight across the code instead of a code. That failure is angle-dependent and it passes desk testing: gloss and lamination covers what to do about it.
So the useful pairing is coated for edge definition, matt or silk for the finish. Gloss buys a code nothing it needs and costs it something it depends on.
Recycled and kraft: coarse fiber and a raised floor
Recycled stock and kraft board are good choices for reasons unrelated to scanning, and both make scanning harder in the same two ways.
The fiber is coarser and less consistent, with visible specks through the sheet, and a speck sitting in a light gap reads as a dark pixel. Local thresholding absorbs a lot of that, but less when the gap is only a few pixels wide to begin with. Small modules on speckled stock is the combination that fails.
And the sheet is not white. Recycled grays, kraft browns and natural tints raise the light end of the range, the same squeeze as uncoated but further along. Black on kraft generally works and often looks better than black on white. Black on kraft at fifteen millimeters with a long URL inside it generally does not. The fix for both is size, and only size.
Grammage is a poor guide to any of this, since a 300 gsm uncoated board absorbs more ink than an 80 gsm coated sheet. It decides one thing only, and it catches people on double-sided work: a light sheet is not fully opaque, so a dark panel on the reverse shows through into the light modules and the margin around them. The quiet zone can be geometrically perfect on the front and useless because of what is behind it.
Why the same file scans from one printer and not another
Three processes treat the same stock differently, and every difference lands on module edges.
Toner, meaning most office and digital printers. Powder fused to the surface with heat. It barely penetrates, so dot gain stays low and edges stay crisp even on uncoated.
Inkjet. Liquid ink, and on uncoated stock it wicks. This is where the effect is largest, and it is why a code that prints acceptably from the office laser goes soft coming off a wide-format inkjet onto uncoated paper.
Offset and flexo. Prepress compensates for gain on both as routine, but that compensation is tuned for images and type rather than for a grid where the gaps carry the meaning. Flexo onto corrugated or unbleached board carries the most gain of anything in normal use, which is why packaging has rules of its own.
So a proof proves something only if it ran on the process and the stock the job will use. An inkjet proof of an offset job tells you the layout is right and nothing more.
The number to protect is module size
Everything above reduces to one question: how wide is a single module on the finished sheet? Divide the printed width by the module count across, which is 21 for a version 1 symbol, 25 for version 2, and four more per version after that. Ink spread is roughly fixed for a given stock and process, so the smaller that number gets, the larger a share of it the spread consumes.
Three levers, in the order they are worth pulling:
- Shorten the encoded string. Fewer characters means a lower version, fewer modules, and a physically larger module at the same printed size. Nothing else is this cheap.
- Make the code bigger, then check it against scan distance in the size guide rather than against how it looks in the layout.
- Do not reach for error correction. More redundancy means more modules, which means smaller ones, and on absorbent stock that trade runs backward. Codes built here sit at the highest of the four levels already, so the lever is pulled and the only thing left to protect is module size. The error correction levels set out what it costs.
Send vector if the printer takes it. An SVG or EPS has no resolution to lose, so the only softness left comes from the paper rather than the file; resolution for print covers the raster case. One setting to change before exporting: our generator ships with a two-module quiet zone on a slider running from zero to eight, and the specification asks for four. Two is fine on a white screen and the first thing to go wrong on a tinted, textured or double-sided sheet.
The test, which takes five minutes
Ask the printer for a sample of the exact sheet, then print the real code onto it.
- Look at the print under a loupe, or a phone camera at maximum zoom, and hunt for light gaps that have closed. If two dark modules have merged anywhere, the code is too small for that stock.
- Scan from the distance a real reader will stand at, not at arm's length over a desk, with the reverse printed too if the piece is double-sided.
- Use two phones from different makers, older one first. A code that reads on one and not the other is a marginal code, not a phone problem.
- If it still fails, size up before changing anything else, then work through the other causes.
Paper is not a detail to settle at the end. It sets the minimum size the code is allowed to be, and that decision belongs in the layout.

