Skip to content
UseQR
ESC

↑↓ MOVE↵ OPEN48 PLACES

Spec & internals

Decode a QR code by hand

To decode a QR code by hand, orient the symbol using its three finder patterns, read the 15-bit format information (XOR with 101010000010010), apply the named mask to the data region, read modules in zig-zag order, de-interleave the codewords, verify with Reed–Solomon, then parse mode indicators to recover the text.

View as MarkdownPaste this page into any AI assistant — it is plain, portable Markdown.

What you need

A printed or on-screen code, graph paper, and patience. Pick a small symbol — a version 1 (21 × 21) or version 2 (25 × 25) code — because the work grows with the square of the side. This page is the reverse of encoding "hello world" by hand; doing both once teaches you more about QR than any amount of reading.

Copy the symbol onto graph paper as a grid of dark and light cells. The side length tells you the version: modules per side = 17 + 4 × version, so 21 means version 1, 25 means version 2, and so on.

Step 1 — orient and read the format information

The three finder patterns belong at top-left, top-right and bottom-left. If the empty corner is anywhere but bottom-right, rotate (or un-mirror) your copy.

The 15-bit format information runs along the edges of the top-left finder. Read it, then XOR with the fixed masking constant 101010000010010 (0x5412) — the spec applies this so the format field is never all zeros. The first two resulting bits give the error correction level, the next three name the mask:

EC bits Level
01 L
00 M
11 Q
10 H

The remaining 10 bits are BCH error correction over the field itself. A second, identical copy sits split beside the other two finders — read it if the first is damaged.

Step 2 — remove the mask

Every data module was XORed with a mask pattern at encode time. Apply the same pattern again to undo it: for mask 0, flip every module where (row + column) mod 2 = 0; each of the eight masks has a similar coordinate formula. Only data and error-correction modules are masked — leave finders, timing patterns, alignment patterns and the format information untouched.

Step 3 — read the zig-zag

Data is stored in two-module-wide columns, starting at the bottom-right corner, running upward, then down the next pair, alternating. Within each pair read right cell then left cell. Skip every function-pattern module and skip column 6 entirely (the vertical timing pattern) — the walk simply steps over it. Dark = 1, light = 0. Group the bits into 8-bit codewords.

Step 4 — de-interleave and check Reed–Solomon

For version 1 there is a single block, so the codewords are already in order. Larger versions interleave codewords from multiple blocks — codeword 1 of block 1, codeword 1 of block 2, and so on — and you must deal them back out using the block table for your version and level.

A full Reed–Solomon check means computing syndromes over GF(256): all zeros confirms an undamaged code. By hand, most people (reasonably) assume a cleanly printed code is intact and move on.

Step 5 — parse the bit stream

Now read the data codewords as a bit stream:

First 4 bits Meaning
0001 Numeric mode
0010 Alphanumeric mode
0100 Byte mode
1000 Kanji mode
0000 Terminator — stop

After the mode indicator comes a character count (8 bits for byte mode at versions 1–9), then the data itself — in byte mode, each 8 bits is one character. Decode until you hit the terminator; everything after it is padding (the alternating bytes 11101100 and 00010001).

If you want to check your work, point /scan at the code, or feed the payload to /validate and compare.

FAQ

How do I know which mask a QR code used?

Read the 15 format bits beside the top-left finder pattern, XOR them with 101010000010010, and take bits three to five of the result. They name one of the eight mask patterns, each defined by a simple coordinate formula.

Can you really decode a QR code without a computer?

Yes, for small versions. A version 1 code holds at most 152 data bits, and every step — unmasking, the zig-zag read, mode parsing — is pencil-and-paper arithmetic. Verifying the Reed–Solomon codewords by hand is the only genuinely tedious part.

Why do the bits I read look like random noise?

You probably forgot to remove the mask. Raw modules are XORed with a mask pattern precisely so they look balanced and noise-like; undo the mask named in the format information before reading the zig-zag.

What order are QR code bits stored in?

In two-module-wide columns starting from the bottom-right corner, running alternately up and down, right cell before left cell, skipping all function patterns and the vertical timing column. Bits group into 8-bit codewords, most significant bit first.

Try it — free, no signup