What we tested
This site has generators for more than 30 kinds of code, from QR codes to EAN barcodes, and a camera-based barcode reader. So we checked every format: can the site’s own reader actually read the codes the site makes?
The short version:
- 18 of the 23 formats tested could be read.
- The 5 that failed have no decoder in the reading library at all.
- Along the way we found and fixed two faults on our side: the reader was set up so that it could not read Codabar (NW-7), and UPC-E could not be read because of bugs in the library.
- With the centre of a QR code covered, codes stopped reading at a smaller area than the nominal figures (about 7% for L up to about 30% for H) suggest.
Method
- Render each code with the same library the site’s generators use (bwip-js 4.11.2).
- Read the image with the same library the site’s reader uses (the JavaScript port of ZXing, @zxing/library 0.23.0).
- Count it as read when the decoded text matches the original.
We tried four bwip-js scale factors, 1 to 4. At scale 1 the narrowest bar of a 1D barcode is 1 pixel wide and one module of a 2D code is 2 pixels. Every format that could be read was read even at scale 1. We also tried each image rotated by 90°.
These are clean images generated on a computer, with no blur or tilt. A camera adds focus errors, glare and curved paper, so real-world reading is harder than this. Treat any format marked unreadable here as one a camera will not read either.
Result 1: which formats could be read
2D codes
| Format | Read | Rotated 90° | Notes |
|---|---|---|---|
| QR Code | Yes | Yes | |
| Micro QR | Yes | Yes | |
| Data Matrix | Yes | Yes | |
| Aztec | Yes | Yes | |
| PDF417 | Yes | No | Must be read the right way round |
| MaxiCode | Yes | No |
1D barcodes
| Format | Read | Rotated 90° | Notes |
|---|---|---|---|
| Code 128 | Yes | No | |
| GS1-128 | Yes | No | AI brackets are not part of the result0104901234567894 |
| SSCC-18 | Yes | No | Read as GS1-12800049012345000000013 |
| Code 39 | Yes | No | |
| Code 93 | Yes | No | |
| Codabar (NW-7) | Yes | No | Failed before this test (see below; fixed) |
| ITF | Yes | No | |
| JAN / EAN-13 | Yes | No | |
| EAN-8 | Yes | No | |
| UPC-A | Yes | No | |
| ISSN | Yes | No | Read as an EAN-13 starting 9779770317847001 |
| UPC-E | Yes | No | Failed before this test (see below; fixed) |
| Code 11 | No | — | No decoder |
| MSI | No | — | No decoder |
| Pharmacode | No | — | No decoder |
| Japan Post customer barcode | No | — | No decoder |
| Royal Mail 4-State | No | — | No decoder |
Why some formats failed
ZXing has no decoder at all for Code 11, MSI, Pharmacode, the Japan Post customer barcode or Royal Mail 4-State. These are used where dedicated scanners are the norm — factory parts tracking, drug packaging and postal sorting — and the built-in scanners on phones rarely support them either.
Bug found 1: UPC-E could not be read
UPC-E can be read by the original Java version of ZXing. Reading the JavaScript port showed three faults in its UPC-E path:
- The code that assembles the decoded digits throws the assembled string away instead of returning it to its caller.
- The search for the end of the barcode looks for the EAN/UPC-A end pattern rather than UPC-E’s own six-module one.
- Expanding the code back to twelve UPC-A digits, which is where the check digit comes from, compares character codes instead of digits, so it always expands the wrong way.
We wrote a corrected UPC-E decoder on our side and use it whenever the library reads nothing (20 September 2026). It was verified against eight generated UPC-E codes, covering both number systems and every branch of the expansion.
Bug found 2: Codabar could not be read
In the first run, Codabar (NW-7) failed too. It turned out that the JavaScript port of ZXing leaves the Codabar decoder out by default unless you list the formats to read. The site’s reader did not list any, so it could not read codes from the site’s own Codabar generator.
The reader now lists every format it can read, and we confirmed Codabar reads correctly (fixed on 19 September 2026). The tables above show the results after the fix.
1D barcodes do not like being rotated
QR Code, Data Matrix, Aztec and Micro QR still read after a 90° turn. None of the 1D barcodes did. The reader scans the image one horizontal row at a time, so once the bars lie horizontally no row crosses them. Among 2D codes, the wide PDF417 and MaxiCode also failed when rotated.
ZXing has a “try harder” mode that also tries rotations, but it is too slow for a reader processing several camera frames a second, so the site does not use it.Hold barcodes so the bars stand upright in the camera view.
Formats whose result differs from the printed text
- GS1-128: the brackets in
(01)04901234567894under the code are for people; the decoded result is0104901234567894. - ISSN: a magazine barcode is an EAN-13 that embeds the 8-digit ISSN after the prefix 977.
0317-8471reads as9770317847001.
Result 2: how much damage a QR code survives
QR codes have an error correction level: L, M, Q and H, each more tolerant of dirt and damage than the last. We encoded a URL (https://heron-tools.com/ja/tools/2dcode/qr/, 43 characters) at each level, placed a white square in the centre, and grew it one module at a time until the code stopped reading.
| Level chosen | Level produced | Version (size) | Largest white square survived | Share of area | Nominal recovery |
|---|---|---|---|---|---|
| L | L | 3(29 × 29) | 6 × 6 | 4.3% | about 7% |
| M | Q | 4(33 × 33) | 12 × 12 | 13.2% | about 15% |
| Q | Q | 4(33 × 33) | 12 × 12 | 13.2% | about 25% |
| H | H | 5(37 × 37) | 17 × 17 | 21.1% | about 30% |
What this shows
- Codes fail at a smaller area than the nominal figure: the “about 7% to 30%” in the standard is the share of codewords (8-bit blocks of data) that can be repaired. Covering a square also breaks every codeword cut by its edge, so a larger share of codewords is damaged than of area. It is safer not to assume that covering 30% of the area still works.
- Choosing M can give you Q: bwip-js, which the generators use, automatically picks a stronger level when the data still fits in the same size (version). For this URL, choosing M produced a Q code: same size, more damage-tolerant.
- H makes the code bigger: for the same URL, L gave 29 × 29 modules and H gave 37 × 37. Printed at the same size, H has smaller modules — about 1.6 times as many per unit of area. Use H when dirt is a worry, such as with a logo overlay or outdoor signs; M is a good default for small prints such as business cards.
We repeated the test with an alphanumeric sentence (HERON TOOLS QR ERROR CORRECTION TEST 2026): L survived a 4 × 4 square, M (produced as Q) 10 × 10, and H 13 × 13 modules. The pattern is the same.
Result 3: how big the same data becomes
We encoded the 16 characters HERON-TOOLS-2026 in each format at bwip-js scale 1 (2 pixels per module for 2D codes, a 1-pixel narrowest bar for 1D barcodes) and measured the result.
| Format | Size (pixels) | Modules |
|---|---|---|
| Data Matrix | 36 × 36 | 18 × 18 |
| QR Code | 42 × 42 | 21 × 21 |
| Aztec | 46 × 46 | 23 × 23 |
| PDF417 | 103 × 27 | — |
| Code 93 | 182 (width) | 182 |
| Code 128 | 200 (width) | 200 |
| Code 39 | 288 (width) | 288 |
- Among 2D codes, Data Matrix was the smallest, about 14% narrower than the QR code. That is why it is used for tiny markings on parts and circuit boards.
- Among 1D barcodes, Code 39 came out about 1.4 times as wide as Code 128, because Code 39 spends more bars on each character.
- 1D barcode height can be set freely, so only width is compared.
Summary: choosing a format
- Anyone’s phone should read it: QR Code. For retail products, EAN/JAN.
- It must print small: Data Matrix (2D) or Code 128 (1D).
- Code 11, MSI, postal barcodes and the like: specialist formats required by a business partner or postal service. Assume ordinary phones cannot read them.
- Dirt or damage is likely: use QR error correction level H, accepting a larger code.