Encode the same string twice and the QR code can look different. That is the effect of the mask pattern. This article covers why masking exists, the eight condition formulas, how one of them is selected, and how a reader reverses it.
1. Why masking is needed
The data region of a QR code is simply the encoded string plus its error correction codewords laid out as black and white modules. The problem is the visual pattern that results.
- Long runs of one colour make it hard for the reader to count module boundaries. Telling 20 white modules from 21 in a camera image is genuinely difficult.
- Large solid blocks skew local brightness and throw off the binarisation threshold.
- Shapes that resemble a finder pattern inside the data region make the reader misjudge the corners and therefore the orientation of the code.
You cannot change the input string. Encode "abcabcabc..." and a regular pattern appears. So a way is needed to change the appearance without changing the data. That is masking.
2. Masking is a reversible flip
Masking is simple: feed the module position (row i, column j) into a formula and flip the colour of every module where the condition holds. Nothing is encrypted; a position-based pattern is overlaid.
Apply the same formula again and every flipped module flips back. Because applying it twice restores the original, the reader needs no special inverse operation.
The scope matters. Masking touches only the data and error correction region. It never touches:
- Finder patterns at three corners and their separators
- Alignment patterns (more of them in larger versions)
- The timing pattern (the alternating line)
- Format information and version information
These are the markers used to locate, orient and size the code. Flipping them would remove the starting point of decoding.
3. The eight condition formulas
Eight mask patterns are defined, numbered 0 through 7. With row i and column j counted from the top-left corner at (0, 0), a module is flipped when the condition is true.
1: i mod 2 = 0
2: j mod 3 = 0
3: (i + j) mod 3 = 0
4: (i div 2 + j div 3) mod 2 = 0
5: (i × j) mod 2 + (i × j) mod 3 = 0
6: ((i × j) mod 2 + (i × j) mod 3) mod 2 = 0
7: ((i + j) mod 2 + (i × j) mod 3) mod 2 = 0
Here div is integer division and mod is the remainder. Each formula breaks up the pattern differently.
- 0 produces a checkerboard: finely scattered, but still very regular.
- 1 gives horizontal stripes and 2 vertical stripes, so they suit different kinds of runs in the source data.
- 4 flips in 2×3 blocks, giving a coarse grid.
- 5 through 7 involve multiplication, so their periods are long and the result looks irregular.
Eight exist so that every input has at least one good match. With a single pattern there would always be inputs whose own regularity lines up with it and produces the worst case.
4. How one is selected
The encoder applies all eight and computes a penalty score for each. Lower is better, and the lowest scoring mask is used.
Penalties come from four rules.
- Runs of the same colour: five or more identical modules in a row or column are penalised, with more points the longer the run.
- 2×2 blocks of one colour: solid squares are penalised, since larger blocks make binarisation less stable.
- Finder-like sequences: a 1:1:3:1:1 ratio appearing in a row or column carries the heaviest penalty, because it makes the reader misidentify a corner.
- Imbalanced black-to-white ratio: the further the proportion of dark modules is from 50%, the higher the penalty.
The evaluation looks only at appearance, never at the encoded text. That is why the same string can end up with a different mask when you change the error correction level: the codewords change, so the pattern changes.
5. How the reader reverses it
The reader starts with the format information, a 15-bit field next to the finder patterns holding:
- the error correction level (L / M / Q / H, 2 bits)
- the mask pattern number (0-7, 3 bits)
The remaining bits protect those five with error correction and a mask of their own. Nothing can be decoded without the format information, so it is stored in two places in the code; if one is damaged, the other is read.
The order is: read the mask number, apply the same mask to restore the data region, run error correction, then decode.
6. Practical notes
- A different-looking code is not a bug. The same URL with a different error correction level or version can select a different mask. Compare by decoding, not by eye.
- Do not pin the mask by hand. Overriding the penalty evaluation for design reasons lowers the scan rate. Adjust colour, quiet zone and size instead.
- Logo overlays rely on error correction, not masking. The two are unrelated mechanisms.
- When debugging, start at the format information. Whether it decodes tells you if the problem is in locating the code or in the data region.
Summary
Masking is a reversible operation that makes a QR code easier for machines to read. One of eight candidates is chosen automatically by scoring runs, blocks, finder-like sequences and colour balance, and the chosen number is recorded in the format information.
To try it in the browser, use the QR code generator; to inspect a code that will not scan, use the QR analyzer or QR restore tools. Related reading: QR versions and modules, finder patterns, and error correction.
Frequently asked questions
Can I choose the mask pattern myself?
The specification allows any of the eight patterns. In practice an encoder applies all eight and picks the one with the lowest penalty score. Forcing a specific mask can lower the scan rate, so leave it to automatic selection unless you have a specific reason.
Does masking destroy the original data?
No. Masking only flips modules at positions determined by a formula, and applying the same mask twice restores the original arrangement. The mask number is stored in the format information so the reader can reverse it.
Are the finder patterns masked as well?
No. Masking applies only to the data and error correction region. Finder patterns, alignment patterns, timing patterns, format information and version information are never flipped, because the reader needs them to locate and orient the code.