Most QR sizing arguments start with pixels. They should start with the encoder's mode choice, because the mode decides how many bits each character costs, and that decides which version the symbol needs.
The generator's status line makes the order visible. The same characters, at the same error correction level, can land on different versions.
Four modes, four prices
QR does not store text. The encoder splits the message into segments and picks a mode for each one (ISO/IEC 18004 §6.3.6 allows mode changes inside a single symbol):
| Mode | Alphabet | Cost |
|---|---|---|
| Numeric |
0–9
|
10 bits per 3 digits |
| Alphanumeric |
0–9, A–Z, space and $ % * + - . / :
|
11 bits per 2 characters |
| Byte | any byte | 8 bits per byte |
| Kanji | JIS X 0208 characters | 13 bits per character |
The byte row is where most non-Latin text lands. A Chinese, Japanese or Korean character usually takes 3 bytes in UTF-8, and a character outside the Basic Multilingual Plane takes 4. The 2,953-byte limit counts bytes, not characters.
The upper-case URL
HTTPS://ZEROTOOL.DEV at level L fits version 1, 21×21 modules, because the string uses only alphanumeric characters. The lower-case https://zerotool.dev needs version 2 at the same level, 25×25.
Scheme and host are case-insensitive (RFC 3986 §3.1 and §3.2.2), so upper-casing up to the first / of the path is safe. Paths and query strings can be case-sensitive. This one substitution buys a whole version, and versions are not free at print time: 21×21 and 25×25 need different module sizes to fill the same box.
The lower-case form at level M is version 2 with 172 of 224 data bits used (77%), encoded as 20 bytes in byte mode. Two things to read there: how full the symbol is, and that "20 bytes" is the byte count, not the character count.
The maxima
At version 40, level L, one mode only:
| Mode | Maximum |
|---|---|
| Numeric | 7,089 digits |
| Alphanumeric | 4,296 characters |
| Byte | 2,953 bytes |
| Kanji | 1,817 characters |
Byte capacity at version 40 falls as error correction rises: L 2,953, M 2,331, Q 1,663, H 1,273.
These are one-mode maxima. Mixed content pays the segment headers and usually fits less: a URL is alphanumeric for the host and byte for a lower-case path, and the encoder chooses the split that uses the fewest bits.
The failure message is worth reading closely. 2,400 characters of plain text at level M need 19,220 data bits; version 40 at level M holds 18,672, so the text is 548 bits (about 69 bytes) over. The same text fits at level L. The tool reports that instead of cutting the string to make it fit.
Version groups and header cost
Each version adds four modules per side. Version 1 is 21×21, version 2 is 25×25, version 5 is 37×37, version 8 is 49×49, and version 40 is 177×177. A larger version holds more and needs more pixels per module for the same physical size.
Version bands matter as well, because the header is not a fixed size. The capacity check in the test suite subtracts 4 bits for the mode indicator and 8 bits for the character count through version 9, then 16 bits for the count from version 10. That extra byte of header is why capacity tables are grouped by band instead of forming a straight line.
Error correction is not a quality dial
L recovers about 7% of codewords, M 15%, Q 25%, H 30%. Each step adds error correction codewords, and those codewords occupy space that data could use.
At version 1 with level H, 17 digits or 7 bytes still fit; one more byte pushes the same content to version 2. That is the trade in one line: the same text, a bigger symbol.
So "use H everywhere" buys damage tolerance and pays for it in density. If the printed footprint stays fixed, a higher level raises the version, each module gets fewer pixels, and the scan gets harder. The added recovery can be cancelled by the loss of resolution. Pick the level from the surface: M suits most screens and prints, Q or H for codes that get handled, scratched or dirty. Error correction restores damaged codewords; it is not encryption, and beyond its level the message is gone.
Quiet zone arithmetic
The quiet zone is a light margin four modules wide on all four sides (ISO/IEC 18004 §5.3.7). It carries no data and still takes space. A version 2 code with its quiet zone is 33 modules wide: 25 + 4 + 4.
That number explains the PNG sizes. Stretch 33 modules to 1024 px and each module is 31.03 px, so some modules come out 31 px and others 32 px. Round down instead: 1024 px becomes 1023 px at exactly 31 px per module. The SVG is one path on the module grid, so it scales without that rounding.
Verify the symbol, not the preview
Before printing, check four values from the file you will actually send:
- Version and module count. They move together: 25×25 is version 2, 37×37 is version 5, 49×49 is version 8. A small text edit that bumps the version also changes the module count, so a layout built for the earlier symbol can clip the new one.
- Error correction level. Read it from the export, not from the setting you remember.
- Whole pixels per module. Divide the PNG width by the module count plus 8 quiet-zone modules. A non-integer result means uneven modules.
- Decode it. Use the phones you expect people to use.
The resolution floor from the reader's tests: a 33-module code failed at 45×45 image pixels (about 1.4 image pixels per module) and read at 60×60 (about 1.8). That is a camera constraint, separate from print DPI.
Then decode the file and compare the result with what you encoded. The decoder guide explains how a reader decides the mode split and character set, which is the part that changes silently when a payload is edited.
The order of operations follows from the numbers: pick the mode-friendly form of the payload, then the lowest error correction level the surface allows, then the size in whole pixels per module. The QR Code Generator prints version, module count, mode split and bit usage in its status line, so each step can be checked before anything reaches a printer.
Top comments (0)