Codes · interactive
Why does a torn QR code still scan?
Left: the printed card; drag on it to scratch it. Right: the exact image the decoder reads, and its verdict. Below: damaged codewords in each block against what that block’s backup can fix.
1 See
A QR code can lose a whole chunk and still work. Which of its squares are your link, and which are there in case something goes wrong?
2 Change
3 Understand
Model notes and sources
A real encoder. The code is generated on this page, following ISO/IEC 18004 in byte mode: a 4-bit mode indicator, an 8-bit length, the UTF-8 bytes of the link, a terminator and the pad bytes 0xEC and 0x11 fill the data codewords (8-bit bytes). These are split into blocks, and each block gets Reed–Solomon error-correction codewords computed over GF(256) (field polynomial x⁸ + x⁴ + x³ + x² + 1, generator (x − α⁰)(x − α¹)…). The blocks are interleaved and placed in two-square-wide columns that zigzag up and down from the bottom-right corner, data first and then error correction, which is why the backup fills the left two-thirds. One of eight masks is chosen with the standard’s four penalty rules, and the format information is BCH-coded. Because the tool builds the code itself, it knows every square’s job.
This code. https://explainertools.com is 26 bytes. At level H it needs version 4, 33 × 33 squares: 100 codewords, 36 data and 64 error correction, in 4 blocks of 25 (9 data + 16 error correction). A Reed–Solomon block with 2t check codewords can fix up to t wrong codewords when the decoder doesn’t know where the damage is, so each block here fixes 8 of its 25. Of the 1,089 squares, 19% carry the link, 47% are error correction, 26% are function patterns (the three finder patterns and their separators, the timing patterns, the alignment pattern and the format information), and 8% are the mode and length header, padding and 7 spare bits. Levels L, M, Q and H restore about 7%, 15%, 25% and 30% of the codewords. A lower level fits the same link into a smaller code: version 2 at L and M, version 3 at Q.
A real decoder. Damage is drawn onto the printed card: a torn corner (the knob sets the share of the code’s area torn away; the margin goes with it), an opaque dried-coffee stain, a printed logo, or scratches that scrape the ink off. The phone shows the exact camera image that is decoded: the card at 288 px, about 7 px per square, on a dark table. It goes to jsQR 1.4.0 (Apache-2.0), and jsQR’s answer sets ✓ or ✗. We changed one jsQR heuristic: it now accepts an alignment pattern only within 2 squares of where the three finder patterns predict it, and otherwise uses that prediction, as it already did when it found none. Unchanged, it locks onto a random data pattern whenever a tear removes the real alignment pattern, and fails even with only a few codewords damaged. “Codewords damaged” counts the codewords with at least one square that jsQR’s own binarizer now reads as the wrong color. Right at a block’s limit, the count and the decoder can disagree by a codeword or two, because the decoder samples a few edge squares slightly differently.
Not modeled. Blur, glare, perspective, curved or crumpled paper, low light and printing defects. Phone scanners differ: some recover more (for example with smarter location or erasure decoding) and some less. In the film, the tear covers 20% of the code’s area and damages 28 of its 100 codewords; the worst block is at its limit of 8.
Sources: Denso Wave, QR code error correction feature; QR code (Wikipedia); Reed–Solomon error correction (Wikipedia); Project Nayuki, Creating a QR Code step by step; ISO/IEC 18004:2015, QR Code bar code symbology specification; jsQR by Cosmo Wolfe and contributors.
An ExplainerTools Original.


