How Do QR Codes Work? From Black Squares to a Web Page — featured illustration
Technology

How Do QR Codes Work? From Black Squares to a Web Page

6 min read
Create a QR code

A QR code is a grid of dark and light squares your phone reads as bits, entirely on the device and in a fraction of a second. Three corner squares tell the camera where the code is and which way up it sits, error correction rebuilds whatever is smudged or covered, and what comes out is a short piece of text. Your phone then decides what to do with that text based on how it starts, which is why one code opens a browser and another joins a Wi-Fi network.

You point a phone at a square on a restaurant table and a menu loads before your arm is back down. Nothing was typed, nothing was paired, and the square sent nothing anywhere. It just sat there in a very particular arrangement of black and white, and your camera read it the way you read a printed word.

Here is the whole chain, from ink to open page. Each piece has more depth than one section can carry, and several get their own walkthrough elsewhere.

Three corners do the hard part first

Look at any QR code and you will see three identical squares-inside-squares: top-left, top-right, bottom-left. Those are finder patterns, and they answer two questions instantly. Is there a code in this picture, and which way up is it?

Their proportions were chosen deliberately. Draw a line through one in any direction and the run of dark to light comes out 1:1:3:1:1. That ratio is rare in photographs of paper, packaging and tabletops, so software can sweep a camera frame hunting only for it. Find three, and you have three corners of a square. The empty fourth corner tells the decoder how the code is rotated, which is why a code taped sideways to a lamppost scans like one lying flat.

Bigger codes add a smaller alignment pattern near the bottom-right. A code is rarely flat and square in front of a lens: it is curved around a bottle, tilted on a table, shot from an angle. Alignment patterns give fixed reference points so the decoder can un-warp the image into a clean grid.

Between the finder patterns run two dotted lines, one horizontal and one vertical, alternating dark and light. Those are timing patterns. They set the ruler, telling the decoder how wide one cell is.

The grid is just bits

Strip away the corners and the timing lines and what remains is a field of small squares called modules. Dark is one, light is zero. That is the entire storage mechanism.

Those bits get read in a zigzag that starts at the bottom right and snakes upward in two-column strips, then back down the next pair. The first few declare the encoding mode, because QR codes pack different content differently: digits compress tighter than letters, and letters tighter than arbitrary bytes. Then a length count, then the payload.

The payload is text. Always text. A QR code does not contain a website, an image or a file. It holds a short string, and your phone decides what to do with it based on how that string starts. Something beginning with https:// gets handed to a browser. Something beginning with WIFI: gets handed to network settings, which is all a WiFi QR code really is. Something starting BEGIN:VCARD goes to contacts. The pattern itself is dumb. The conventions around it do the work.

Why a coffee ring does not kill it

If the grid held only your URL, one smudged module would break it. It does not. A meaningful share of the modules carry Reed-Solomon error correction data, padding that lets a decoder rebuild what it cannot see from what survived.

You choose how much padding at generation time. Four levels exist, recovering roughly 7%, 15%, 25% and 30% of the code:

  • Level L, around 7%, squeezes the most data into the smallest grid. Fine on a screen.
  • The 15% level, M, is the common default and a reasonable one for print.
  • Q, at roughly 25%, buys headroom for labels that get handled or rained on.
  • Pick H, near 30%, when something will sit on top of the code.

That last case is why a logo in the middle of a QR code works at all. The logo is not part of the data. It covers modules, and error correction quietly reconstructs them. Cover more than the level allows and the code goes dead, which is how a logo that keeps creeping larger kills a design that scanned fine last week.

One more trick is baked in: masking. Raw data sometimes produces big blank patches or accidental copies of the finder pattern, both of which confuse scanners. So the encoder XORs the grid against each of eight standard patterns and keeps whichever comes out most evenly mixed. Two short strips of format information beside the finder patterns record which mask and error correction level were used, so the decoder can undo it.

Bigger codes are not just zoomed in

QR codes come in versions numbered 1 to 40. Version 1 is a 21 by 21 grid. Every step up adds four modules per side, so version 40 lands at 177 by 177. More content means a higher version, and more modules crammed into the same physical square.

This matters in print more than people expect. A version 3 code and a version 25 code on the same business card look the same size to your eye, but the second has modules a fraction of the width. A cheap printer smears them, and a phone at arm's length cannot resolve them. Shortening the encoded URL is often the cheapest fix for a code that will not read. The size guide has the distance-to-size math.

The white margin is part of the specification too, not decoration. It is called the quiet zone and runs about four modules wide on every side. Codes butted against a colored panel or a page edge fail constantly for that one reason.

What happens after the camera

The decode takes a fraction of a second and runs entirely on the device. Roughly: turn the frame into black and white, hunt for the 1:1:3:1:1 ratio, lock onto three finders, sample every module, read the format bits, remove the mask, run error correction, reassemble the bytes, produce a string.

Only then does anything visible happen. Your phone shows you the string, or a preview of it, and waits for a tap. That pause is the whole security layer, because the scanning step treats a sticker somebody slapped over a parking meter as faithfully as the real thing underneath.

If the string is a link, tapping opens a browser and the ordinary web takes over. A static code sends you straight to whatever address the creator typed. A dynamic code sends you to a short redirect first, which logs the scan and forwards you on. That extra hop is the only reason dynamic codes can be edited and counted after they are printed. The squares never change. What waits at the end of the redirect does.

Where it actually goes wrong

Nearly every failure traces to the physical layer rather than the math. Not enough contrast between modules and background. An inverted code, light on dark, which plenty of scanners refuse. A missing quiet zone. Ink bleed that fattens the dark modules until the grid smears together. Glare on a laminated table tent. A code printed at half the size its viewing distance needs.

If you have one that stopped working, start with contrast and margin before anything else. Those two explain most of it, and the diagnosis order from there takes about five minutes.

Share this article