How QR codes actually work

Updated 2026-08-26 Β· about 8 minute read

A QR code looks like noise. It isn't β€” every region of that square has a defined job, and once you can name the parts, most of the mystery goes away. This is a walk through what each part does, written while building a QR encoder from scratch, which is a very effective way to discover exactly which details matter.

The anatomy of a QR code

Look at any QR code and you can pick out its structure:

  • The three big squares in the corners are finder patterns. They are the first thing a scanner looks for, and their ratio of dark to light bands (1:1:3:1:1 along any line through the centre) is deliberately unlike anything that occurs in ordinary images. Three corners rather than four is what tells the scanner the code's rotation, so it reads correctly upside down.
  • The dotted lines running between two finder patterns are the timing patterns. They alternate dark and light, giving the scanner a ruler to measure module size against β€” essential when the code is photographed at an angle.
  • The smaller squares that appear in larger codes are alignment patterns, which correct for perspective distortion when the code is on a curved surface or shot off-axis.
  • The strip beside the finders holds format information: which error-correction level was used and which mask was applied. It is stored twice, in two different places, because if you cannot read it you cannot read anything else.
  • Everything else is data and error correction, laid out in a zig-zag that starts at the bottom-right and snakes upward in two-column strips.

The blank margin around the outside is not decoration either. It is the quiet zone, and it is part of the specification β€” four modules wide. Scanners use it to find where the code ends. Crop it off and many scanners simply will not see the code at all.

How data is actually stored

Text is converted to bytes, then those bytes are laid into the grid as bits: dark for 1, light for 0. Before the data comes a short header saying which encoding mode is in use and how many characters follow.

The mode matters for capacity. QR codes have a numeric mode that packs three digits into ten bits, an alphanumeric mode covering capitals and a few symbols, and a byte mode that handles anything at eight bits per byte. A code containing only digits holds roughly three times as much as one containing arbitrary text. This is why a phone number encodes into a tiny code and a long URL with tracking parameters produces a dense one.

Capacity also depends on version β€” the size of the grid, from version 1 at 21Γ—21 modules up to version 40 at 177Γ—177. The encoder picks the smallest version that fits your data at your chosen error-correction level.

Why a damaged code still scans

This is the genuinely clever part. QR codes use Reed–Solomon error correction, the same family of maths that protects CDs, DVDs and deep-space transmissions. Redundant codewords are computed from your data and stored alongside it. If some modules are unreadable β€” a coffee stain, a fold, a sticker β€” the decoder can reconstruct the missing values from what survived.

You choose how much redundancy to spend:

  • L β€” recovers about 7% damage. Smallest, sparsest code.
  • M β€” about 15%. The usual default.
  • Q β€” about 25%. Worth it on packaging and labels.
  • H β€” about 30%. Necessary if you plan to put a logo over the middle.

That last point explains the logo trick: covering the centre of a QR code with a brand mark works not because there is a special "logo area", but because those covered modules count as damage, and level H has enough redundancy to absorb them. Keep the logo under roughly 30% of the area and always test the result before printing a thousand of them.

Higher error correction is not free. The redundant codewords occupy space, so the same text at level H produces a physically denser code than at level L. You are trading data capacity for resilience.

The masking step nobody mentions

Here is a detail almost no explainer covers. After the data is placed, the encoder does not stop β€” it applies a mask, flipping modules according to one of eight mathematical patterns.

Why? Because some data produces awkward-looking grids: large blank areas, or accidental sequences that resemble a finder pattern. Both confuse scanners. The encoder generates all eight masked versions, scores each with a penalty function that punishes long runs of one colour, large same-colour blocks, finder-like sequences and an unbalanced dark/light ratio, then keeps the lowest-scoring one. Which mask was used is recorded in the format information, so the decoder can undo it.

The practical consequence: encode the same text twice with different settings and you can get visibly different-looking codes that both decode identically. Nothing is wrong β€” they simply chose different masks.

The four reasons yours won't scan

Nearly every unscannable QR code fails for one of these:

  1. The quiet zone is missing. Someone cropped tight to the code or placed it flush against other artwork. Leave four modules of blank margin.
  2. Not enough contrast. Dark on light, with a real difference in lightness β€” not two mid-tone brand colours. Inverted codes (light modules on a dark background) fail on a meaningful share of scanners, so avoid them even though some readers cope.
  3. Printed too small for the viewing distance. A rough rule is that the code should be at least one tenth of the distance it will be scanned from: about 2 cm for a phone held 20 cm away, but 10 cm for a poster read from a metre back.
  4. Too much data. A long URL with campaign parameters forces a higher version, which means smaller modules at the same physical size. Shorten the link first β€” this is the single most effective fix.

If you are troubleshooting, generate the same content at a lower error-correction level and a shorter URL and compare density. You can usually see the problem.

Are QR codes safe?

The code itself is just text β€” it cannot contain a virus. The risk is entirely in where it sends you, and that risk is real: "quishing" attacks work by pasting a malicious code over a legitimate one on a parking meter, restaurant table or payment terminal.

Sensible precautions: check the URL your phone previews before opening it, be suspicious of a code stuck on top of another, and never enter payment details on a page you reached by scanning a code in public. On the other side, if you are the one distributing codes, prefer a static code containing your real URL over a redirect through a third-party shortener β€” your code then keeps working even if that service disappears, and people can see where it goes.

Make one

Everything above is implemented in QR Code Generator β€” pick an error-correction level, adjust the quiet zone, and download as PNG for screens or SVG for print. It runs entirely in your browser, so nothing you encode is sent anywhere, and the codes never expire.

If you are putting a code on something, you may also want Image Resizer for getting the artwork to the right size, or URL Encoder if your link has parameters that need escaping properly. The full set is on Pikkit.

Try the tool

Frequently asked questions

Can a QR code contain a virus?

Not directly β€” a QR code holds text, most often a URL. The danger is the destination, not the code. Always check the URL your phone previews before opening it, especially with codes displayed in public places.

Why do some QR codes have a logo in the middle?

Because error correction lets the decoder reconstruct covered modules. Generating at level H, which tolerates about 30% damage, leaves enough redundancy to cover the centre with a logo and still scan.

Do QR codes expire?

A static code containing your data directly never expires. Codes from services that route through their own short link stop working if that service shuts down or moves the feature behind a paywall.

What is the maximum amount of data in a QR code?

At the largest version, roughly 7,089 digits, 4,296 alphanumeric characters or 2,953 bytes at the lowest error-correction level. In practice, anything near that produces a code too dense to scan reliably from a phone.

Why does my QR code look different each time I generate it?

The encoder tests eight masking patterns and picks whichever scores best for your particular data. Different content, or a different error-correction level, can select a different mask, so the pattern looks different while decoding identically.