For months my QR generator's default PNG output was a flat 256px, and it worked — because everyone encoded short URLs. A typical short link lands in version 2–3 of the QR spec, roughly 25–29 modules across, so 256px gave ~8–10px per module and every phone scanner read it instantly.
Then a user encoded a marketing link with tracking params — around 180 characters. The encoder picked a version 8 symbol: 49×49 modules. At 256px that's barely 5px per module, and my rasterizer antialiased the edges, so adjacent modules bled into each other. The image still looked like a perfectly normal QR code. It just wouldn't scan. Desktop scanners mostly managed; my phone failed about half of attempts at a normal distance.
The lesson I'd missed: scanability is a function of pixels per module, not the image's nominal size. A 256px version 2 code and a 256px version 8 code are not the same product. On top of that, decoders expect a quiet zone — four modules of white margin on every side — and my tight viewBox cropped it by default, which only mattered once someone pasted the image onto a colored page.
The fix had two parts. First, I compute output width from the symbol itself: module count × 8px plus quiet-zone padding, so a version 8 code now renders around 570px instead of failing silently. Second, SVG became the recommended output for anything embedded in a page, since it scales with none of this aliasing. I also exposed the error correction level after seeing users stamp labels over codes — level H tolerates 30% obstruction, level L almost none.
Rule I now apply to every image-generating tool I build: don't size output by a fixed pixel default; size it by the structural content of what you're rendering.
I ended up packaging this as a small API at https://x402.freeq.one/tools/qr_generator.html, where the sizing rule now runs server-side — a long URL simply returns a wider PNG.
Top comments (0)