QR Code Error Correction Levels Explained (L, M, Q, H)
A friend asked me to fix a QR code that had stopped working on her cafe receipts. The printer had switched paper stock, the ink was bleeding slightly into the white squares, and roughly one in three customers complained that the menu would not load. The codes were technically intact, but the scanner could no longer tell the dark modules from the light ones with enough confidence. The fix took me thirty seconds in a QR code generator - I bumped the error correction level from L to H, regenerated the codes, and the complaints stopped the same day. The data on the codes never changed. Only the error correction did. That little dropdown menu nobody reads is doing more work than most people realise, and it is built on top of an algorithm from 1960 that quietly powers half the barcoded world.
Table of Contents
- 1. What QR Error Correction Actually Means
- 2. The Reed-Solomon Algorithm in Plain English
- 3. The Four Levels - L, M, Q, H
- 4. The Capacity Trade-off Nobody Explains
- 5. Blocks, Interleaving, and Why Damage Patterns Matter
- 6. Logos, Artistic QR Codes, and Why H Exists
- 7. How to Choose a Level for Your Code
- 8. Common Mistakes With Error Correction
- 9. How to Test a Code at Each Level
- 10. Frequently Asked Questions
Most QR code articles mention error correction in a single paragraph and move on. The advice is usually a recycled "use M for general purposes, H if you have a logo," which is fine until you actually run into a situation where the default does not work. This guide is the version I wish I had read before printing 500 cafe receipts. It is long, but every section earned its place by being the thing that fixed a real problem.
What QR Error Correction Actually Means
A QR code is not just a picture of your data. It is your data plus a layer of mathematical redundancy that lets a scanner reconstruct the original even when part of the image is missing, smudged, glared out by a reflection, or covered by a logo. The error correction system is what makes that reconstruction possible. Without it, a single dust speck in the wrong place would make a code unreadable.
The redundancy is added by the generator at encoding time. When you type a URL into a QR generator, the tool first turns that URL into a sequence of bytes, then runs those bytes through an algorithm that produces a separate sequence of "error correction codewords." Both groups - the data codewords and the correction codewords - get baked into the same pattern of black and white squares. When you scan the code, the decoder reads everything, checks the correction codewords against the data, and either confirms the data is clean or works out which bytes are wrong and repairs them on the fly.
The amount of redundancy that gets added is exactly what the error correction level controls. Pick a low level and you get a smaller code with less wiggle room. Pick a high level and you get a denser code that survives more abuse. Everything else - module size, contrast, quiet zone, scanning distance - matters too, but the error correction level is the one you actively choose, and it decides what kind of damage your code can shrug off.
The Reed-Solomon Algorithm in Plain English
The maths under QR error correction was published in 1960 by two researchers at MIT Lincoln Lab named Irving Reed and Gustave Solomon. Reed-Solomon codes were originally aimed at things like deep space communication and reading scratched CDs, and they ended up almost everywhere a system has to survive partial data loss. QR codes use them too, with one tweak - they operate on 8-bit codewords (one byte each), which fits cleanly into the way QR encodes binary data.
Here is the short version of how it works without the maths. Suppose your message is 16 bytes long. The algorithm treats those 16 bytes as the coefficients of a polynomial. It then evaluates that polynomial at a few extra points and adds those extra values - the correction codewords - to the end of the message. If the receiver sees a corrupted version of the full sequence, they can use those extra points to figure out which bytes drifted from their original values and put them back where they belong.
The clever part is that Reed-Solomon does not just detect that something is wrong. It can locate the bad bytes and repair them, all from the same set of codewords. Each pair of correction codewords lets it fix one byte of damage. So if you have 20 correction codewords, the algorithm can repair up to 10 corrupted bytes without ever knowing in advance which bytes those were going to be.
A useful intuition
Think of your message as a unique line drawn through a set of points. If you know enough points along the line, you can rebuild the whole line even if a few are wiped out. Reed-Solomon is doing the same thing with higher-dimensional polynomial fits over a special number system called a Galois field. The "field" part just means the maths works cleanly inside a fixed range of byte values - no overflow, no rounding, no information leaking out the edges.
You will sometimes see references to GF(256) in QR documentation. That is the byte-sized Galois field Reed-Solomon operates in. The QR specification fixes the generator polynomial at x^8 + x^4 + x^3 + x^2 + 1, which encoders and decoders agree on so they always interpret bytes the same way. You do not need to understand the polynomial to use a QR generator, but it is the reason every scanner on the planet can read the codes your generator produces, regardless of who wrote the software.
The Four Levels - L, M, Q, H
Every QR code uses exactly one of four error correction levels, baked into the format information strip that surrounds the finder patterns. The level determines the ratio of correction codewords to data codewords. More correction means more recoverable damage, but it also leaves less room for your actual data inside the same physical code.
| Level | Letter code | Approx. damage recoverable | Typical fit |
|---|---|---|---|
| Low | L | Around 7% of codewords | Clean digital screens, indoor displays |
| Medium | M | Around 15% | General use, the default for most generators |
| Quartile | Q | Around 25% | Printed materials that may get worn or marked |
| High | H | Around 30% | Codes with logos, outdoor signage, rough handling |
Those percentages refer to codewords, not pixels. The number of recoverable codewords is fixed by the level, and codewords map to roughly eight modules each, depending on the QR version. In practical terms, a Level H code can usually survive having about a third of its surface obscured or damaged, while a Level L code falls apart after about a tenth.
The naming is older than the standard and it always confuses people on first read. L, M, Q, and H stand for Low, Medium, Quartile, and High. "Quartile" gets used instead of a fourth letter because the level recovers up to a quarter of the codewords. The order is fixed in the QR specification and every generator and scanner uses the same letter codes, so you can pick L on one tool and decode it on another without surprises.
The Capacity Trade-off Nobody Explains
Here is the part that catches people off guard. Higher error correction does not just make the code "more robust" in some abstract way. It actively eats into your data capacity. A QR code of a given physical size and version has a fixed number of codewords total. The more of those go to correction, the fewer are left for your data. So bumping a code from L to H without changing the data forces the encoder to use a larger QR version, which means more modules, smaller squares for the same physical print size, and a denser pattern overall.
A simple example. Take a URL like https://stackconvert.com/qr-generator - 38 characters. At Version 2 (25x25 modules), it just fits at Level L. The same data at Level H needs to jump up to Version 3 or 4 because the encoder has to make room for those extra correction codewords. The physical code is now larger or, at the same physical size, the modules are smaller and harder for a phone camera to resolve.
| Level | Max characters at Version 10 (alphanumeric) | Max bytes at Version 40 (binary) |
|---|---|---|
| L | 513 | 2,953 |
| M | 395 | 2,331 |
| Q | 283 | 1,663 |
| H | 213 | 1,273 |
Move from L to H and you give up around 60% of the storage. That is fine for a short URL where you have plenty of headroom, but it can mean a real-world difference for a vCard contact card with 300 bytes of contact details, or a WiFi credential block with a long password. Tools like the StackConvert QR generator handle the version selection automatically once you pick a level - you do not have to do the maths - but understanding the trade-off helps you decide when to compromise on data length to keep the code clean.
Blocks, Interleaving, and Why Damage Patterns Matter
Reed-Solomon has one limitation worth knowing about. It can only fix a fixed number of errors inside a single block of codewords. If you have a block that supports 8 byte repairs and 9 bytes in that block go bad, the whole block fails, even if the rest of the code is pristine. A coffee stain that happens to land entirely inside one block is much worse than the same amount of damage spread evenly across the code.
The QR specification works around this by splitting bigger codes into multiple blocks and interleaving them. Instead of writing block one, then block two, then block three in order, the encoder weaves the codewords together so that adjacent modules in the printed code usually belong to different blocks. A stain that wipes out a 1cm patch of the code now affects a handful of codewords from many blocks rather than dozens from one block. Each block has room to repair its own share, and the whole code survives.
You will rarely have to think about this at the user level, but it is the reason QR codes are remarkably tolerant of localised damage - a tear, a sticker corner, a hand partially blocking the view. They are less tolerant of "global" damage like uniform fading, blur, or low resolution, because that hits every block equally. If you are printing on a surface that fades over time, the error correction level helps. If you are scaling the code down below readability, no level will save you.
Logos, Artistic QR Codes, and Why H Exists
The most common reason to choose Level H is to put a logo in the middle of the code. The placement is deliberate - the data area is read in a snake-like pattern that loops around the finder patterns, and the centre is one of the few regions where you can punch a hole without taking out the corner alignment markers. But that hole is essentially damage to the code, and the scanner has to reconstruct the missing data using error correction.
The rough rule of thumb is to keep the logo to no more than 25-30% of the code area at Level H, and never overlap any of the three large finder squares in the corners. Some tools let you push past those limits visually, but every additional pixel of logo eats into your error correction budget. If you also have damage from printing, the code can suddenly fail on phones that were able to scan it before.
Logo size in practice
At Level H, a logo covering about 20% of the code is a safe target. That gives you visible branding without consuming the entire repair budget. The other ~10% of redundancy stays available for real-world damage - fingerprints, fold lines, slight ink bleed. If your code lives entirely on a phone screen, you can creep closer to 25%. If it is going to be printed and handled by customers, stick with 20% or below.
Decorative codes that use coloured modules, custom corner shapes, or replaced patterns push the limits harder. The artistic QR codes you see in advertising campaigns are usually generated at Level H with a custom rendering layer that knows which modules carry data and which are stylistic. Without Level H, those codes would be unscannable. With Level H, they get away with enough creative damage that the marketing team is happy and the codes still work. There is a wider tour of those campaigns in the QR code marketing guide if you want the design-side perspective.
How to Choose a Level for Your Code
The decision is usually obvious once you describe where the code is going to live and how it will be scanned. Here is the playbook I run through in my head before I pick a level.
Pick L when
The code is on a clean digital surface, scanned at close range, never printed, and there is no logo. Think a code displayed inside a mobile app, a slide in a presentation projected onto a screen, or a payment confirmation page. Level L gives you the smallest, most data-efficient code and the scanner conditions are good enough that you do not need much redundancy.
Pick M when
You are not sure and you do not have a logo. M is the universal default for a reason. It handles indoor print, restaurant menus, business cards, basic packaging, and pretty much anything that does not involve a logo or harsh environments. If you are generating one-off codes for general use, M is almost always the right call. The free QR code generator guide walks through this default in more detail.
Pick Q when
The code is printed and will get handled, scuffed, taped, or aged. Q is the underused middle option. It is the right pick for printed shipping labels, food packaging, museum signs, and product hangtags. You get more headroom than M without losing the data capacity you would at H. If your codes are getting wear-and-tear failures at Level M, jump to Q before going all the way to H.
Pick H when
There is a logo in the centre, the code is going outdoors, or you simply cannot afford for it to fail. H is right for storefront window codes that get rained on, outdoor advertising, restaurant table cards near food spills, and any branded code with a logo. The data capacity penalty is real, but for a short URL it is barely noticeable and the reliability win is enormous.
A quick decision tree
Does it have a logo? → H. Is it going outdoors or onto something that gets handled hard? → H. Is it printed and might get a bit of wear? → Q. Is it printed but treated reasonably? → M. Is it strictly on a screen with no logo? → L. When in doubt, M.
Common Mistakes With Error Correction
A few patterns come up over and over when I help people debug codes that are failing in the wild. They all stem from misunderstanding what error correction does and what it does not.
Picking H by default to "be safe"
H sounds like the conservative choice, but defaulting to H without thinking about data length pushes you into a denser code unnecessarily. A short URL at Level H prints fine. A 200-character URL or a full vCard at Level H produces a much denser code than the same data at Level M, and a denser code is harder to scan from a phone held at arm's length. If you do not have a logo and you are not in a harsh environment, you are penalising scanability for no real benefit.
Treating the level as a substitute for size
Error correction repairs damage. It does not fix codes that are too small to be read in the first place. A 1cm code at Level H is still a 1cm code. If you are putting it on a poster scanned from three metres away, no level of error correction makes it work. Size and module resolution come first; error correction handles the random degradation on top.
Ignoring the level on dynamic codes
Dynamic QR codes (the ones with a redirect service in the middle) usually have short URLs, which means there is plenty of room to bump up to Level Q or H without changing the visible density. People often leave them at L because the encoded URL is small, and then the printed code fails in the real world. If the redirect URL is short, the error correction level is almost free, so use it.
Putting a logo over the finder patterns
The three big squares in the corners are not data, they are the scanner's anchor points. Error correction cannot replace them, regardless of level. A logo that creeps over a finder pattern will make the code unscannable on most phones no matter how high you set the level. Keep logos centred and away from the corners.
How to Test a Code at Each Level
Testing a code at the level you are about to use is the cheapest insurance you can buy before a print run. I do this even for codes I am sure will work, because the cost of being wrong is reprinting a batch. The process I follow:
- Generate the code at the level you intend to use. Print one copy at the actual production size, on the actual production material if you can.
- Scan it with three phones - ideally an older Android, a recent iPhone, and one in between. Different cameras and decoders behave differently at the edges.
- Try scanning from the angle and distance your real users will use. A code that scans perfectly from straight above might fail when held at 45 degrees on a sunlit table.
- If your code will be printed on glossy or laminated material, cover a small corner with a fingerprint or smudge and try again. If the level is right, the code still scans.
- For logo codes, deliberately cover an additional 5% of the code area and confirm it still scans. That is your safety margin against ink bleed or scratches.
If a code fails the smudge test at Level M, bump it to Q and retest. If it fails at Q, go to H. There is no shame in iterating - the right level depends on the surface and the phones, and you cannot always predict that from the spec alone. The thirty seconds of regeneration are nothing compared to a wasted print run.
One last thing worth mentioning. The QR specification also protects the format information strip (which encodes the level itself) with a separate error correction system called BCH. So even if the corner where the level is encoded gets a smudge, the scanner can still figure out which level the code was generated at. That redundancy is invisible to users but it is the reason scanners almost never report a "cannot determine error correction level" error.
Frequently Asked Questions
Can I change the error correction level after a code is printed?
No. The level is baked into the code at generation time, just like the data. To change the level you have to regenerate the code and replace the printed version. This is the same constraint that applies to changing the URL on a static code - once it is printed, it is locked.
Does a higher level mean a larger image?
Not necessarily a larger image, but a denser one. For the same data, a higher level forces the encoder to use a larger QR version, which has more modules. If you print at the same physical size, each module is smaller. So in practice, yes, you usually want to print higher-level codes slightly larger to keep the modules legible to phone cameras.
Is Level H always better than Level L?
For damage resistance, yes. For everything else, no. Level H gives up data capacity, produces denser codes, and asks more from the printer and the scanner. If your scenario does not need the extra resilience, Level H is overkill and may actually hurt scan reliability by making modules smaller.
Why do most generators default to Medium?
Because M sits in a sweet spot for general use. It survives most real-world damage, it does not eat much data capacity, and it produces a code at a sensible density. Generators err on the side of "good enough for most users" and M fits that brief. If your scenario is unusual, override it.
Can the same data produce different codes at different levels?
Yes, and they will look visibly different. Same URL, four levels, four distinct patterns. The data section of the code is the same conceptually, but the surrounding correction codewords differ, the chosen mask pattern often differs, and the version may differ. All four are valid codes that scan to the same URL.
Does the level affect how fast a phone scans the code?
Marginally. A denser code (higher level or more data) takes a fraction longer for the camera to focus and the decoder to read, but on modern phones the difference is barely perceptible. The level matters far more for whether the scan succeeds at all, not how fast it succeeds.
What level do payment QR codes use?
Most payment QR codes - the kind printed on a merchant card or displayed on a terminal screen - use Level M or Q. They have to balance scan reliability with the short data payload of a merchant ID and amount. Some banks specify the level in their integration docs; if you are building a payment integration, follow whatever the network says.
Does Reed-Solomon work the same way in other systems?
The core algorithm is the same. Reed-Solomon shows up in CDs, DVDs, hard drives, DSL modems, deep space communication, and barcode formats like Data Matrix. The parameters - field size, generator polynomial, block layout - differ between systems, but the underlying idea of evaluating a polynomial at extra points to enable repair is shared. QR is one of the most visible everyday uses of the algorithm but far from the only one.
Should I generate codes locally or use an online service?
If the data is sensitive - a WiFi password, a private URL, a contact card with personal details - generate the code in a tool that runs locally in your browser rather than sending the data to a remote server. The StackConvert QR generator runs entirely client-side, which means your data never leaves your device. For a deeper look at the WiFi case in particular, the WiFi QR code generator guide covers the format details and the security trade-offs.
Wrapping Up
The error correction level is one of those settings that most people never touch, and then one day a code fails in production and they spend two hours figuring out why. The short version of everything above: M is a sensible default, H is required if you have a logo or outdoor exposure, L only makes sense on screens, and Q is the underrated middle option for printed materials. Reed-Solomon does the heavy lifting underneath, and once you understand what it is repairing, the choice usually picks itself.
When you are ready to generate one, the StackConvert QR generator lets you pick the level, see the resulting code, and download a print-ready image without uploading your data anywhere. Test it at the size and surface you will actually use, scan it from a couple of phones, and you will know the level is right long before the print job goes out.