· 2 min read
A QR code from scratch: Reed-Solomon, masks and proving it right
How a zero-dependency QR encoder in TypeScript is built, and four independent ways to check that the matrix is correct.
qr-svg has no runtime dependency at all: the whole of ISO/IEC 18004 for versions 1 to 40 lives in src/qr/. The interesting part is not generation but proof.
What the encoder consists of
- The GF(256) field and its arithmetic.
- Reed-Solomon encoding: correction bytes as a polynomial division remainder.
- Block interleaving for versions with several blocks.
- Patterns (finder, alignment, timing), format and version information.
- Eight masks and penalties to pick the best one.
How correctness is checked
- Standard references. The ISO example "01234567" in 1-M and the "HELLO WORLD" walkthrough in 1-Q match in the correction bytes.
- An independent implementation. Matrices are compared bit for bit with
qrcode-generator(a devDependency): 12 texts × 4 levels and 80 random strings. - A third-party decoder. jsQR reads all 160 version × level combinations except 23-L, which the reference does not read either (the matrices are identical, a test records this).
- An own decoder that checks zero Reed-Solomon syndromes, independent of the division in the encoder.
Masks
Automatic mask selection often differs from the reference: penalties are computed slightly differently, both masks are valid. So there is deliberately no "picked the same mask" test. The comparison uses the mask taken from the reference's format information.
A caveat
There is no ready-made matrix from the standard itself in the repository: comparison with an independent implementation plays that role. No Kanji, ECI, mixed segments or Micro QR. 45 tests; under a load with a quarter of heavy requests (version 37), p50 is 131 ms and p95 272 ms.