QR Codes, Explained
After reading this you will understand how a QR code packs text into a grid of black and white squares, how error correction lets a scratched code still scan, and how to pick a size and correction level that actually works in print.
A QR code is a two-dimensional barcode: a square grid of black and white cells that encodes text. Point a camera at it and you get back a string, usually a URL like https://example.com. The grid you see is not decoration. Every cell is a bit, and the pattern includes finder squares, timing lines, format bits and redundant recovery data.
Here is the hook. Take any working QR code and cover up to 30 percent of it with a logo or a coffee stain. If it was made at error-correction level H, it still scans. That resilience is built into the math, and this article shows you where it comes from.
What a QR code actually stores
A QR code stores a byte string plus enough extra bytes to repair errors. The visible grid is organized into fixed patterns and data:
- Finder patterns
- The three large squares in the corners. A scanner uses them to locate and orient the code.
- Timing patterns
- Alternating black and white lines between the finders that fix the module coordinate grid.
- Format and version information
- Small reserved regions that tell the decoder the error-correction level and the mask pattern used.
- Data and error-correction codewords
- The rest of the grid, holding your payload and the Reed-Solomon recovery bytes.
The size of the grid is set by the version. Version 1 is 21 by 21 modules. Each version up adds 4 modules per side, so version 2 is 25 by 25, version 10 is 57 by 57, and version 40 is 177 by 177. The library picks the smallest version that fits your data at the chosen error-correction level.
When to use one, and when not
QR codes are worth it when a person will point a phone at a physical or on-screen thing. Restaurant menus, event tickets, Wi-Fi join codes, contact cards on a business card, and payment links are all good fits. The reader gets a long or awkward string without typing it.
They are a poor fit when the destination is already easy to type or when the code will be tiny and low contrast. A 12-character coupon code printed as plain text beats a QR code that a phone struggles to focus on. For linear product identifiers such as EAN or UPC, use a one-dimensional barcode instead; see the Barcode Generator.
Do not shrink a QR code below the size a camera can resolve. A rough rule is that each module should print at least 0.4 mm wide, and the whole code needs a quiet zone (blank margin) of at least 4 modules on every side. Drop the quiet zone and many scanners fail even on a clean code.
The capacity and error-correction math
The total number of codewords in a version is split between data and error correction. If a version has n total codewords and reserves 2t of them for correction, Reed-Solomon can repair up to t corrupted codewords:
Here n is the total codewords in the block, k is the number of data codewords you supply, and t is how many wrong codewords can be fixed. Each codeword is one byte. The four error-correction levels set the ratio of recovery bytes:
| Level | Recovery target | Data share |
|---|---|---|
| L | ~7% | highest |
| M | ~15% | high |
| Q | ~25% | medium |
| H | ~30% | lowest |
The trade is direct. At the same version, moving from L to H replaces data bytes with recovery bytes, so the same message needs a larger, denser grid. Level M is the everyday default because 15 percent recovery survives normal print wear without bloating the code.
A worked example with the demo data
Encoding a short URL at level M
The demo generates a QR code for a URL using the field defaults: level M error correction, byte mode. Work through the sizing for a 25-character link such as https://abc.tools/qr-code.
- The payload is 25 bytes. Byte mode needs a mode indicator and a character-count field, adding a few bits of overhead, so plan for about 27 codewords of data.
- Version 1 at level M holds 16 data codewords. That is not enough for 27, so version 1 is rejected.
- Version 2 at level M holds 28 data codewords and 16 error-correction codewords, for n = 44 total in one block.
- Your 27 data codewords fit inside 28, so version 2 is chosen. The grid is 25 by 25 modules.
- Correction capacity is t = \lfloor (44 - 28)/2 \rfloor = 8 codewords. Up to 8 of the 44 bytes can be wrong and the code still decodes.
So the same 25-character link at level H would need more recovery bytes, spill past version 2, and produce a larger grid. That is the cost of surviving a bigger overlay.
Reading and decoding a code
Decoding reverses the process. A reader locates the three finder squares, corrects for perspective and rotation, samples each module as black or white, reads the format bits to learn the level and mask, un-masks the grid, then runs Reed-Solomon to repair any errors before extracting the byte string.
The mask step matters more than it looks. Before finalizing, the encoder XORs the data region with one of eight mask patterns and keeps the mask that avoids large solid blocks and false finder-like shapes. This keeps the code readable and helps the camera. When you decode an image on this page, the decoder reads the stored mask index and reverses it automatically. Everything runs locally in your browser, so the uploaded image never leaves your machine.
A decoded string tells you the payload, not its safety. A QR code that reads https://example.com could point anywhere. Read the full URL before you open it, especially on a printed code in a public place where a sticker could have been placed over the original.
Common mistakes
Inverting the colors. Most scanners expect dark modules on a light background. A light-on-dark code sometimes works and sometimes does not, depending on the reader. If contrast is low, decoding fails. Keep a luminance difference of at least 40 percent between foreground and background.
Cropping the quiet zone. Designers often trim the white margin to fit a layout. The 4-module border is part of the specification. Without it, the scanner cannot separate the finder patterns from surrounding artwork.
Over-encoding. Putting a 400-character message in a code forces a high version with tiny modules. Shorten the payload. Link to a page rather than embedding a whole vCard when the code will be printed small.
Logo too large. A center logo works only because error correction repairs the covered modules. At level M you have roughly 15 percent to spend. Cover more and the code stops scanning. Raise to level Q or H before adding a big logo, and re-test on a real phone.
Related tools
For linear retail codes, the Barcode Generator handles EAN, UPC and Code 128. If you need filler graphics while building a layout around a code, the Placeholder Image Generator makes sized boxes with labels. To pack many small images into one file, see the Sprite Sheet Generator & Splitter. For stylized black-and-white output from photos, the Dithering Tool and the ASCII Art from Image tool are both here.
Frequently asked questions
What is the maximum a QR code can hold?
Version 40 at level L holds about 2,953 bytes, or roughly 4,296 alphanumeric characters. In practice you rarely go past a few hundred characters, because the module density beyond version 10 makes reliable scanning harder.
Why did my code get bigger when I changed a few characters?
Capacity comes in discrete version steps. Crossing the data-codeword limit for a version forces the next version up, which adds 4 modules per side. A single extra character can trigger that jump.
Which error-correction level should I use?
Level M for screens and clean prints. Level Q or H when the code carries a logo, will be printed small, or lives outdoors where wear is likely. Level L only when you must squeeze in maximum data and the surface is protected.
Can a QR code contain Wi-Fi credentials?
Yes. The payload uses a text format like WIFI:T:WPA;S:MyNetwork;P:mypassword;;. A phone camera recognizes the prefix and offers to join the network without typing the password.
Does adding a color gradient break scanning?
It can. Scanners threshold each module to black or white, so a gradient that lowers contrast in one region raises the failure rate. Keep the darkest foreground clearly darker than the lightest background across the whole code, and test before you print.