The headline: a “22% logo” covers about 5.7-8.3% of the code

The logo size control is a width, not an area. Set it to 22% and the logo spans 22% of the code’s width, which works out to roughly 5.7-8.3% of the actual modules once the white pad around it is counted. That gap is why so much advice about logo size is wrong.

What we did

We generated 248 QR codes with the live generator on this site, sweeping the logo size from 10% to 40% across eight different payloads, and decoded every single one with two independent decoders. Everything else was held at the setting most likely to succeed: near-black on white, square modules, error correction level H, the white pad behind the logo switched on, and the largest output size the generator offers.

The question was simple. At what point does a centre logo stop being readable, and does the answer depend on what you encoded?

It does, and not in the direction most people expect.

The results

“Safe” here means both decoders read the code correctly, and every larger logo setting for that code failed at least one of them. A single lucky pass above the cliff does not count.

Maximum usable logo size by QR code size. 248 samples, two decoders.
Code size Bytes encoded Max safe logo Modules covered Count First failure
21×21230%11.1%49 of 44131%
29×291333%14.4%121 of 84134%
29×292133%14.4%121 of 84134%
33×333034%15.5%169 of 108935%
41×414338%21.5%361 of 168139%
45×455732%14.3%289 of 202533%
49×496836%18.4%441 of 240137%
49×497635%18.4%441 of 240136%

Two of those rows are the same code size reached by two different payloads. They landed on the same threshold, which is the kind of agreement that makes the rest of the table believable.

Finding 1: small codes tolerate less, not more

The 21×21 code - the smallest a QR code can be - topped out at 30%. The 49×49 code took 36%. More data in the code meant more tolerance for a logo, not less.

That is backwards from most people’s intuition, and the reason is visible in the data. A logo is a fixed fraction of the image, so on a small code it lands on proportionally more of the structure that error correction cannot rebuild. At the point where the 21-module code first failed, the white pad was already overlapping 21 modules of its finder patterns - the three big squares a scanner uses to locate and orient the code. No amount of error correction recovers those, because they are what the scanner needs before error correction starts.

The practical version: if you want a big logo, encode a longer URL, not a shorter one. A short link makes a small code, and a small code has less room to give.

Finding 2: the “30% error correction” figure is not an area budget

You will read everywhere that level H error correction lets you obscure 30% of a QR code. Every code we tested failed well before 30% of it was covered. The most tolerant code in the set died with only 21.5% of its modules under the logo.

The 30% figure is real, but it describes codewords - the 8-module blocks the data is stored in - not the picture. Two things eat the difference. Function patterns (the finders, the timing lines, the format information) are not covered by error correction at all. And a centred square block wipes out whole codewords rather than scattering damage thinly, which is the worst case for a Reed-Solomon code.

So “30% can be covered” is best read as a ceiling that nothing in practice reaches, rather than a target to design against.

Finding 3: the size control is a width, not an area

This is the one that trips people up. Because the logo is a square measured across, doubling the number on the slider roughly quadruples the area it takes out.

What the logo size setting actually costs you, measured across all 248 samples.
Logo size settingShare of the code’s modules covered
10%1.1% - 2%
15%2% - 4.5%
20%5.7% - 7.4%
22%5.7% - 8.3%
25%8.3% - 11.1%
30%11.1% - 15.5%
35%17.2% - 20.7%
40%26% - 27.4%

This generator caps the control at 30%, which costs 11.1-15.5% of the modules - comfortably inside what every code in the test survived. The default of 22% is well below that again.

A finding we did not go looking for: the quiet zone

OpenCV refused to read a single one of the 248 codes on the first pass. Not the ones with big logos - all of them, including codes with a 10% logo that jsQR read without complaint.

The cause was not the logo. A QR code is specified to sit inside a quiet zone: four modules of blank space on every side. At the time of the test our own exported PNG had none - the dark modules ran right to the edge of the image. Adding four modules of white to each sample made every one of them decode, and the numbers in this article come from those padded images so that the measurement is about the logo and nothing else.

We fixed it. Downloads from this site now carry the full four-module quiet zone in PNG, JPG, SVG and EPS. (The PDF always had one: it centres the code on an A4 page with a wide margin.) The same OpenCV test that refused every code before the change reads the current export straight off the disk with no help at all.

The practical lesson holds for any QR code, whoever made it: when you place a code on a poster, a menu or a label, leave clear space around it. A code printed hard against a border, a photo or a block of colour is fighting its scanner before the logo is even considered. It is also why the same code can work on screen and fail in print.

Method, so you can check it

  • Source of the codes. live qr-studio.app generator driven through CDP. Not a reimplementation - the same code path a visitor downloads from, driven through the browser.
  • Held constant. Foreground #111111, background #ffffff, square modules, error correction H, white pad on, 512px output. Each of these is the most forgiving option, so the thresholds are upper bounds.
  • Swept. Logo size 10-40% in 1% steps, across eight payloads from 2 to 76 bytes. The product caps the control at 30%; the cap was raised in the browser only, so the failures beyond it could be measured.
  • Decoders. jsQR 1.4.0 (in page, live canvas) and OpenCV QRCodeDetector (offline, exported PNG). Independent implementations, so agreement between them is a property of the code rather than of one library’s binarisation.
  • Coverage figures are counted, not calculated: for every sample we counted how many module centres fall inside the logo’s padded box.
  • The threshold rule is deliberately conservative, as described above.

Download the full dataset (CSV, 248 rows) - every sample, both decoders, coverage counts and all.

What this does not tell you

These are software decoders reading clean digital images. A phone camera is a different instrument - it has autofocus, exposure control and a human tilting it around, and in practice phones often read codes that a strict library refuses. Treat these thresholds as a conservative floor rather than the point where a code becomes unusable.

Everything here was measured on near-black modules on white. Coloured codes behave differently, and the decoders we used are unreliable judges of colour: in earlier testing a neutral dark grey at 9:1 contrast scored badly with a decoder while scanning perfectly off a real phone. Colour deserves its own experiment rather than a footnote in this one.

The two decoders disagreed on 12 of 248 samples, all of them clustered at the edge. That disagreement is the honest shape of the answer: near the limit, whether a code reads stops being a property of the code and starts being a property of the scanner.

Try it on your own code

The generator on this site puts a logo in the middle of a QR code for free, with no sign-up and no watermark, and it picks error correction level H for you as soon as you add a logo. The numbers above are why the size control stops where it does.

Make a QR code with your logo →

What about colour?

We ran the same kind of test on colour: 48 codes put through simulated bad scans to find where contrast starts to cost you, why raising error correction does not rescue a weak colour, and why an inverted code is a gamble on the scanner. Read the colour test →

Keep reading

Related pages on QR Studio.