Ask a QR generator for an 80 × 80 pixel image and you often get a 63 px symbol floating in extra white, or a 58 px one with a wide border. That is not a resizing bug — it is the QR specification working. A symbol may only scale in whole pixels per module, and it must carry a quiet zone at least 4 modules wide. Only certain image sizes exist at all. This page gives the arithmetic that predicts the file you will actually get, one worked example, and the decision for fixed slots, print layouts and machine-read labels.
Why the margin changes with the size you ask for
An encoder does three things before it ever looks at your pixel size. It compresses the data and picks the smallest version that holds it at the selected error correction level: version 1 is 21 × 21 modules, and each later version adds 4 modules per side, up to version 40 at 177 × 177. It surrounds the symbol with a quiet zone of at least 4 modules on all four sides — generators call it margin, whitespace or border, and it is part of the specification, not decoration. Then it scales: to keep every module identical, one module becomes a whole number N of pixels.
The error correction level matters because it moves the version. The four levels recover roughly 7% (L), 15% (M), 25% (Q) and 30% (H) of damaged codewords. Higher recovery reserves more codewords for correction, so the same text can land in version 1 at level M and version 3 at level Q — a jump from 21 to 29 modules per side and a completely different set of legal image sizes. Pin the level in your generator, or the output size changes whenever the payload does.
So the inconsistent margin is simply padding absorbing the difference between the size you typed and the largest legal scaling below it. The generator is not resizing your symbol to fit; it is refusing to stretch modules.
The formula and the legal size ladder
| Version | Symbol modules | With 4-module zone | Legal image sides (N = 1, 2, 3 …) |
|---|---|---|---|
| 1 | 21 | 29 | 29, 58, 87, 116, 145 px |
| 2 | 25 | 33 | 33, 66, 99, 132 px |
| 3 | 29 | 37 | 37, 74, 111 px |
| 10 | 57 | 65 | 65, 130, 195 px |
With the zone switched off, drop the 8: version 1 then scales at 21 × N — 21, 42, 63, 84 px — which is exactly where the 63 and 84 steps in a zero-margin test come from. Micro QR has its own ladder, starting at 11 × 11 modules with a 2-module zone.
Worked example: 123456789 at level M into an 80 × 80 pixel canvas
Inputs:
- Data: the nine digits 123456789, the test string behind this question.
- Error correction: level M, 15% recovery, a common default for industrial labels. Version 1 holds 34 numeric digits at level M, so nine digits land in version 1: 21 × 21 modules.
- Quiet zone: 4 modules per side, the specification minimum and the usual generator default.
- Requested canvas: 80 × 80 px.
Rule in words: the zone is part of the image and every module must become the same whole number of pixels, so the largest legal scaling is the largest N with 29 × N ≤ 80.
Arithmetic:
- 80 ÷ 29 = 2.76 → N = 2
- code plus zone = 29 × 2 = 58 px
- padding = 80 − 58 = 22 px of extra white
Result: an 80 × 80 file whose real content is a 58 × 58 symbol, centred with 11 px of surplus white per side. Nothing is stretched; the symbol inside is perfectly scaled.
The same request with the margin set to 0: 80 ÷ 21 = 3.81 → N = 3 → 63 px of code plus 17 px of padding. The next step, 21 × 4 = 84 px, is already over the request. That reproduces the observed 63 and 84 behaviour exactly.
Decision: if the size is yours to choose, order 87 px (N = 3) or 116 px (N = 4) from the ladder and the file uses every pixel you asked for. If the 80 px slot is fixed, ship the padded file — the code inside is legal and exact, and surplus white only helps decoding.
Interpolation and mixed module widths: what each one costs
Two shortcuts exist for hitting an arbitrary size, and they are not equivalent.
A fractional scale keeps hard edges but unequal modules. An 80 px version 1 without a zone needs 80 ÷ 21 ≈ 3.81 px per module, so rows come out mostly 4 px wide with some 3 px. One such file still decoded with a mainstream open-source decoder in a published test. Bilinear interpolation goes further and blends pixel values: every module edge becomes a grey gradient two pixels wide, and the decoder's binarization threshold then decides where each edge really sits — move the threshold and the geometry moves with it. Shrinking hurts faster than enlarging: squeeze a 58 px version 1 file to 50 px and each module averages 1.7 px with grey edges, a common recipe for an empty decode result.
| Output method | Module width in the file | Edges | Added decode risk | Use it for |
|---|---|---|---|---|
| Integer scale + padding | N px, uniform everywhere | Hard black/white | None | Anything a machine reads |
| Mixed widths (fractional scale) | N and N+1 px | Hard, unequal | Low but per symbol; test the exact file | Fixed slots that cannot take padding |
| Bilinear / bicubic interpolation | Fractional, grey gradients | Soft, threshold-shifted | Real, grows as the ratio leaves an integer | On-screen previews only |
| Vector (PDF / SVG) | Set at print time | Exact at any size | None | Print masters |
The rule: anything a scanner or camera must read keeps whole modules — N px per module, everywhere, with the padding doing the rounding. Interpolated rasters are for humans looking at screens.
What padding costs, and what a lost quiet zone costs
Extra white is the safe direction. A zone larger than 4 modules never degrades decoding; it only consumes label area, so budget it like any other feature dimension. The costly direction is white that disappears: a zone cropped below 4 modules, or neighbouring print, a logo or a second code sitting inside it, reads as dark modules and breaks the finder patterns. Keep the zone inside the image file so later crops cannot remove it — the same 4-module rule that protects a generated file protects a camera image downstream, as covered in cropping barcode images while preserving quiet zones.
Printing and reading it back: the same modules measured twice
Pixels become printer dots at the print engine: at 300 dpi, N = 3 prints one module at 3 ÷ 300 in = 0.254 mm, and the whole version 1 symbol with its zone spans 29 × 0.254 mm ≈ 7.4 mm. Dot gain and ink spread are real at that scale, which is why printed codes get graded rather than assumed — the barcode reader verification for label inspection routine makes print quality measurable instead of hopeful.
At the camera the ladder inverts. A reader does not need a whole number of pixels per module — it samples continuously — but keep at least 2 px across each module, with 3 to 5 comfortable. Below roughly 2 px the edge lands between pixels and the threshold decides the geometry. The quiet zone has to sit inside the field of view with nothing else printed in it.
The numbers are kind. Over a 25 mm field, a 2560 px sensor sees about 26 px across a 0.254 mm module (2560 ÷ 25 × 0.254). Spread the same sensor over a 200 mm tray and it still finds about 3 px per module. Focus, angle and surface contrast decide whether those pixels decode — module size, reading angle and reader distance get their own treatment, and very large modules bring their own quirk, covered in downscaling before barcode decoding.
For tight print on small labels, short focus and polarization earn their keep:
- Interface
- Fast Ethernet
- Resolution
- Up to 3 MP
- Shutter
- Global
- Lens mount
- M5.8
Compact readers with an M12-mount body slot into cramped brackets:
- Interface
- Fast Ethernet
- Resolution
- Up to 3 MP
- Shutter
- Global
- Lens mount
- M12
When one exposure has to cover a whole tray of codes, sensor resolution buys pixels per module:
- Interface
- Fast Ethernet
- Resolution
- 5 MP
- Sensor format
- 1/1.7"
- Shutter
- Global
The decision for each case
- Size is yours to choose: generate on the ladder, side = (25 + 4 × version) × N px, with N picked so the print lands at your target millimetres.
- Fixed pixel slot (HMI tile, PDF frame, UI thumbnail): keep whole modules and let the generator pad. The file is legal and the code inside is exact — an 80 px canvas holding a 58 px symbol is the correct output, not a defect.
- Print size fixed in millimetres: choose an integer number of printer dots per module — 6 dots at 300 dpi = 0.508 mm, within 2% of a 0.5 mm target — then read the px-per-module back at the camera.
- Machine-read code in a slot that cannot move: mixed widths only, and only after the exact file decodes with the exact reader model that will sit on the line.
Checks before the label goes to print
- Zoom the output file to 1:1 and look at module corners: every edge crisp, no grey transition, no mixed widths unless you ordered them.
- Measure the white border in modules: at least 4 on all four sides, and nothing else printed inside it.
- Decode the file itself — the PNG you will actually ship — with the same decoder family the line uses, not a different one on your desk.
- Print a sample and read it at the working distance: at least 2 camera px per module in the live image, zone in frame.
- Pin the error correction level and, where the generator allows it, the version, so a longer payload cannot silently jump the symbol to a bigger version and change every downstream size.
If you are sizing the read side as well, the fixed code readers category filters by resolution, mount and interface. Or send the label dimensions and working distance through the contact page and we will work the optics with you. The code-reading topic page collects the rest of the read-side failure patterns.
Questions
How wide does a QR code quiet zone have to be?
Four modules on all four sides, measured in the symbol's own modules. A 3 px-per-module file therefore needs at least 12 px of white per edge; a 10 px-per-module file needs 40 px. More white is legal and harmless to decoding. Less white, through cropping, tight placement or print inside the zone, is the most common self-inflicted read failure, so keep the zone inside the image file itself.
Why did my generator return 63 px when I asked for 80 px?
The symbol had 21 modules and the margin was set to zero, so the largest whole number of pixels per module fitting in 80 px is 3: 21 × 3 = 63 px, with the remaining 17 px issued as white padding. The next step, 21 × 4 = 84 px, exceeds the request. With the standard 4-module zone the same 80 px request returns 58 px, because 29 modules at 3 px would need 87 px.
What image sizes are legal for a version 1 QR code?
Any side length of 29 × N pixels when the 4-module quiet zone is included: 29, 58, 87, 116, 145 px and up. With the zone removed the ladder is 21 × N: 21, 42, 63, 84 px. Version 2 adds 4 modules per side, so its ladder is 33 × N: 33, 66, 99, 132 px. Versions reach 40 at 177 modules per side, so a version 40 code with its zone scales in steps of 185 px.
Is bilinear interpolation acceptable if I need an exact pixel size?
For an image a person looks at, yes. For a code a machine decodes, no: interpolation turns each module edge into a grey gradient, and the decoder's binarization threshold then decides where every edge sits, shifting the geometry by fractions of a module. Mixed module widths, some rows N px and some N+1, keep hard edges and often decode, but standardize on that only after the exact file passes the exact reader that will read it.
Does a margin larger than the 4-module minimum hurt anything?
Decoding: no. Four modules is the floor, and extra white on all sides only consumes label area. The risks all run the other way: a zone cropped or placed below 4 modules, printing or marks inside the zone, modules too small for the print process, or too few camera pixels per module at read time. Size the zone generously, keep it quiet, and spend your concern on module width instead.


