The Verification Venue · three robustness regimes in one square

A Third of It Is Already Forgiven

A QR code carries its own repair kit. Scribble out a chunk of one and a phone still reads it, because up to about a third of the data is spare on purpose. But that forgiveness is not spread evenly, and some squares are not forgiven at all. Take a marker to the real, live code below and watch a real decoder decide.

The spare capacity is Reed-Solomon error correction over the finite field GF(256), exactly as the QR standard ISO/IEC 18004 lays it out. The code below is drawn by a genuine encoder in this page; the verdict is a genuine decoder running on the modules you damage. Nothing here is a picture of a QR code or a faked scan.

Drag on the code to smear damage across it. This is a real, standard QR code: a phone camera can scan it off the screen.

Loading the live QR instrument.

SCANS Undamaged. The decoder reads the payload cleanly:
0 modules damaged

Byte mode. The version bumps up automatically to fit.

Error correction level
Damage mode

Errors is the honest everyday case: the scanner cannot see where the wound is, so each fix costs two codewords. That is where the famous 30% comes from.

0 paints a single module (for nicking one sacred square); larger smears a blob.

Version (symbol size)

Versions 3 and 4 split the data into two or four blocks. Watch the budget become several separate bars: that is interleaving, and it changes the game.

The 30% is the pessimistic number

Reed-Solomon works over a field where each byte of the message is one element, and it appends d−1 spare bytes (the error-correction codewords) to every block, giving it a minimum distance of d. A decoder then obeys one inequality, the errata bound:

2 × (errors) + (erasures) ≤ d − 1

An error is a corrupted byte whose position the decoder must first find; that costs two spare bytes. An erasure is a byte the decoder already knows is missing (a cell the scanner sees as blank or occluded); that costs only one. So the same block tolerates twice as many erasures as errors. The quotable 30% is the error ceiling, the unknown-damage case. Flip the damage mode above from Errors to Erasures and the exact same smear that just failed will suddenly hold: you are feeling the factor of two.

The budget is per block, not per code

On a small code the whole payload is one block, so damage anywhere draws on one shared budget. Bigger codes are split into blocks, and the bytes are interleaved across the whole square precisely so a coffee-ring or a scratch is spread thin over many blocks. Switch to Version 3 or 4 and try it: a tight scribble that reddens one block's bar can kill the code while the total damage sits well under 30%, because you overran one block's small share. Spread the same number of hurt cells around and every bar stays green. Same wound count, opposite verdict.

Some squares are sacred

The frame is not forgiven at all

Everything above is about the data region. The three big squares in the corners (the finder patterns) and the dotted lines between them (the timing patterns) carry no data. They are function patterns: they are how a camera finds, orients and samples the grid before any decoding starts. They live outside Reed-Solomon altogether. Hit Nick a finder pattern or Snip the timing line above, with the brush shrunk to a single module, and watch the code die at essentially zero percent damage. It does not degrade gracefully; there is simply nothing left to run the error correction on. Even the 15-bit format information that names the error correction level gets its own separate armor, a BCH(15,5) code stored twice, because if the reader cannot learn which forgiveness budget applies, the budget is useless.

The armor has armor: the format bits

These 15 bits (for the current code) encode the error correction level and the mask. They are protected by a BCH(15,5) code with minimum distance 7, so it corrects up to 3 flipped bits. Click bits to flip them and watch the decoder still recover the right level. Flip a fourth and the armor can finally be beaten.

The same 15 bits are stored a second time on the far side of the code, so a reader that loses one copy uses the other. On the live code above, this is why damaging the strips beside a finder does not, by itself, change which budget the decoder applies.

The four levels, on a Version 1 code

LevelDENSO nominalData cwEC cw t = errorsError ceilingErasure ceiling

Total codewords on a Version 1 symbol: 26. The nominal column is DENSO Wave's and the standard's rounded characterization; the exact correctable fraction is floor(EC/2)/total for errors, and it drifts a little by version. On a Version 1 H code the real error ceiling is 8/26 = 30.8%, and the erasure ceiling is 17/26 = 65.4%.

The check: this verdict is a real decode, not a lookup

Every number on this page is the output of a real Reed-Solomon codec over GF(256) (primitive polynomial 0x11d, the QR field) running in your browser. Press the button to dump the decoder's actual working for the current code and damage: the received codeword vector, the syndromes it computed, and the byte positions it located.

-

The offline gate recomputes all of it from first principles and exits 0 only if every check passes, two independent ways where it can: node research/why-damaged-qr-codes-still-scan/verify-why-damaged-qr-codes-still-scan.mjs. Free choices & scope. Byte encoding mode; Versions 1 to 4 only (the four smallest). The nominal 7 / 15 / 25 / 30% are DENSO's rounded figures, not exact; the true per-symbol error fraction is floor(EC/2)/total. The "scan" verdict models the algebraic decode (does Reed-Solomon recover the block); it does not model a camera's optics, lighting or binarization. Function-pattern damage is treated as fatal because a real reader cannot locate or sample the grid without it; how much finder damage a specific reader tolerates in practice varies, but the principle (it is outside error correction) is exact.

What is exactly true here, and what is a model

Exactly true (the standard). A QR data region is Reed-Solomon coded over GF(256) per ISO/IEC 18004. Each block appends d−1 EC codewords and the decoder obeys 2·errors + erasures ≤ d−1. On a Version 1 symbol (26 codewords) the levels are H = 9 data + 17 EC, Q = 13 + 13, M = 16 + 10, L = 19 + 7, so t = 8 / 6 / 5 / 3 and the error fractions are 30.8 / 23.1 / 19.2 / 11.5%. Finder and timing patterns are function patterns outside the code. Format information is a BCH(15,5) code, minimum distance 7, XOR-masked with 0x5412 and stored twice. The encoder here is bit-for-bit identical to a reference implementation, and its output is read back by an independent decoder (jsQR) during verification.

A model, not a measurement. The instrument decides "scans" by whether the real Reed-Solomon decoder recovers each block within the errata bound. A physical scan also depends on contrast, focus, angle and the reader's own binarization and grid-detection, none of which are modelled. So a code that "scans" here may still fail on a bad photo, and some readers recover from more finder damage than we allow. The algebra is exact; the optics are out of scope.

Named scope. Byte mode only (not numeric, alphanumeric or kanji, which pack tighter). Versions 1 to 4. A centered "logo hole" still eats into one region's per-block budget, so the folklore "always use H for logos" buys room but is not a free pass; try a big logo on a multi-block code and watch one bar redden.