QR code color combinations that scan

We rendered 38 foreground and background colour pairs as real QR codes and ran each one through a QR decoder at four sizes, then blurred, shrunk and noisy. Here is what decoded, with each pair's contrast ratio and hex codes.

This is a software test, not a phone test.

Every result below comes from jsQR, the open-source decoder inside our QR Code Color tool, reading a clean digital image. Phone cameras deal with glare, focus, printing and lighting, and their apps use other decoders. A pass here is a good sign, not a guarantee. A fail here is a strong warning. Test your final code with a few real phones before you print it.

12decoded in all 7 tests
21decoded in some tests
5failed at every clean size

Results for 38 colour pairs

Pass means jsQR returned the exact encoded text. "Noise" shows how many of 3 noise patterns decoded, and it only counts as a pass at 3/3. "Read inverted" means jsQR only read the code on its second, inverted pass (light modules on a dark background).

QRPairContrast400 px200 px120 px80 pxBlurredShrunkNoiseVerdict
Black on white#000000 on #ffffff21.00:1PassPassPassPassPassPass3/3Decodes in every test
Dark grey on white#4b5563 on #ffffff7.56:1PassPassPassPassPassPass3/3Decodes in every test
Mid grey on white#9ca3af on #ffffff2.54:1FailPassPassPassPassPass0/3Decodes in some tests
Light grey on white#d1d5db on #ffffff1.47:1FailFailFailFailFailFail0/3Fails every clean size
Navy on white#1e3a8a on #ffffff10.36:1PassPassPassPassPassPass2/3Decodes in some tests
Violet on white#5b3df5 on #ffffff6.12:1PassPassPassPassPassPass2/3Decodes in some tests
Dark green on white#166534 on #ffffff7.13:1PassPassPassPassPassPass3/3Decodes in every test
Red on white#dc2626 on #ffffff4.83:1PassPassPassPassPassPass2/3Decodes in some tests
Magenta on white#d946ef on #ffffff3.46:1PassPassPassPassPassPass1/3Decodes in some tests
Coral on white#ff6b4a on #ffffff2.82:1PassPassPassPassPassPass2/3Decodes in some tests
Orange on white#f97316 on #ffffff2.80:1PassPassPassPassPassPass1/3Decodes in some tests
Lime on white#84cc16 on #ffffff1.98:1FailPassPassPassFailFail0/3Decodes in some tests
Gold on white#d4af37 on #ffffff2.10:1FailPassPassPassFailFail0/3Decodes in some tests
Yellow on white#facc15 on #ffffff1.53:1FailFailFailFailFailFail0/3Fails every clean size
Pastel pink on white#f9a8d4 on #ffffff1.81:1FailPassPassFailFailFail0/3Decodes in some tests
Light blue on white#93c5fd on #ffffff1.80:1FailPassPassFailFailFail0/3Decodes in some tests
Mint on white#a7f3d0 on #ffffff1.28:1FailFailFailFailFailFail0/3Fails every clean size
Black on yellow#000000 on #facc1513.71:1PassPassPassPassPassPass3/3Decodes in every test
Black on gold#000000 on #d4af379.99:1PassPassPassPassPassPass3/3Decodes in every test
Black on light grey#000000 on #d1d5db14.25:1PassPassPassPassPassPass3/3Decodes in every test
Black on mid grey#000000 on #9ca3af8.27:1PassPassPassPassPassPass2/3Decodes in some tests
Navy on cream#1e3a8a on #fef3c79.30:1PassPassPassPassPassPass3/3Decodes in every test
Brown on beige#78350f on #f5f5dc8.20:1PassPassPassPassPassPass3/3Decodes in every test
Dark teal on mint#134e4a on #ccfbf18.41:1PassPassPassPassPassPass3/3Decodes in every test
Purple on lavender#5b21b6 on #ede9fe7.57:1PassPassPassPassPassPass3/3Decodes in every test
Dark red on pink#7f1d1d on #fecdd37.10:1PassPassPassPassPassPass2/3Decodes in some tests
Grey on grey#6b7280 on #9ca3af1.90:1FailPassPassPassFailFail0/3Decodes in some tests
Pink on light blue#f9a8d4 on #bfdbfe1.28:1FailFailFailFailFailFail0/3Fails every clean size
Blue on red#2563eb on #dc26261.07:1FailFailFailFailFailFail0/3Fails every clean size
Green on red#16a34a on #dc2626 · read inverted1.47:1FailFailFailPassFailFail0/3Decodes in some tests
White on black#ffffff on #000000 · read inverted21.00:1PassPassPassPassPassPass3/3Decodes in every test
White on navy#ffffff on #1e3a8a · read inverted10.36:1PassPassPassPassPassPass3/3Decodes in every test
White on red#ffffff on #dc2626 · read inverted4.83:1PassFailPassPassFailPass2/3Decodes in some tests
Yellow on navy#facc15 on #1e3a8a · read inverted6.76:1PassFailPassPassPassPass2/3Decodes in some tests
Gold on black#d4af37 on #000000 · read inverted9.99:1PassPassPassPassPassPass2/3Decodes in some tests
Cyan on black#22d3ee on #000000 · read inverted11.62:1PassPassPassPassPassPass1/3Decodes in some tests
Red on black#dc2626 on #000000 · read inverted4.35:1PassPassPassPassPassPass0/3Decodes in some tests
Dark grey on black#374151 on #000000 · read inverted2.04:1PassPassPassPassPassPass0/3Decodes in some tests
Download results (CSV)

What we saw

These patterns hold for these 38 pairs, in this test. We don't claim a safe contrast threshold.

How we tested

  1. Encode. The text https://crispkit.app/qr-code-color/ with error correction M. That makes a version 3 code: 29 × 29 modules plus a 4-module quiet zone.
  2. Render. Each pair is drawn by the same QR engine our QR Code Color tool uses, at 400, 200, 120 and 80 px wide. That is about 10.8, 5.4, 3.2 and 2.2 pixels per module.
  3. Degrade. Three extra tests. Blurred: the 200 px image is blurred (a three-pass box blur of radius 1 px). Shrunk: the 400 px image is shrunk to 100 px and enlarged back. Noise: the blurred 200 px image gets random noise of up to ±40 levels per channel. The noise uses 3 fixed seeds, the same for every pair.
  4. Decode. jsQR 1.4.0 reads each image, normal first and then inverted. A test passes only if the decoded text matches exactly.
  5. Contrast. The WCAG 2 contrast ratio of the two colours, from 1:1 (same colour) to 21:1 (black on white).
  6. Repeat. All image processing is plain JavaScript with seeded noise, so a re-run in the same browser gives the same results. We ran it twice and got identical results. You can re-run it above. The test script is qr-color-test.js.

Limits: one decoder, one payload length, flat colours only (no gradients or logos), and no print, camera or lighting effects. A pair that decodes here can still fail on paper under poor light.

Questions

Can a QR code be any colour?

It can be many colours, but the dark modules need to stand out clearly from the background. In our test, all 5 pairs that failed at every clean size had a contrast ratio of 1.53:1 or less, while every dark-on-light pair at 2.8:1 or more decoded at all four clean sizes.

Do inverted (white on black) QR codes scan?

jsQR read each of our light-on-dark pairs in at least some tests, but only on its inverted pass. Support for inverted codes varies between phone scanner apps, so test with the phones your audience uses, or keep dark modules on a light background.

Why do yellow and pastel QR codes fail?

They are too close in brightness to a white background. Yellow on white (1.53:1) and mint on white (1.28:1) failed at every size we tried. Contrast ratio measures brightness difference, and that difference is what decoders rely on.

Is this the same as testing with my phone?

No. This is a software decoder reading clean digital images. It is a quick filter for colour choices, not a substitute for scanning your printed code with real phones.

Guide: Can a QR code be any color?

Free tools for this