Skip to content
All builds

QR Studio

What it does for you

Offline QR generator with logo embed and PNG/SVG export, on a from-scratch encoder (Reed–Solomon, all masks, versions 1-40).

QR Studio screenshot

In more detail

QR Studio is an offline QR code generator that runs in your browser. You type a link or a Wi-Fi login, pick your colors, drop a logo in the middle, and download a PNG or SVG. Nothing gets uploaded, and there's no tracking redirect in between.

Honest limits

There was no local headless browser that day, so there's no UI screenshot. The demo assets are real QR PNGs generated by gen-sample.mjs. It only does byte mode, so URLs pack less densely than they could. The proof that it scans is the decoder in the test file. That's strong evidence, but it's our decoder checking our encoder.

How I built it

This was the last project of the first stretch, back in June when I was still building one at a time with Claude Code. It took about 2.5 hours, which is long compared to what came later.

I set the scope: write the QR encoder from scratch as the headline, and make the logo embed and PNG/SVG export the product. I also set a test-first plan. Claude Code wrote the whole encoder: Reed-Solomon error correction over GF(256), the 160-entry error-correction table, alignment patterns, all 8 masks with penalty scoring, and the format and version bits. Then it wrote an independent decoder inside verify.mjs, so the test reads every generated code back and checks the text matches.

That paid off. With the tests green, we caught a real placement bug before a single failed phone scan: the second copy of the format bits was overwriting the dark module and skipping a cell. Claude's design hook also rejected my display font twice (Fraunces, then Instrument Serif, both on its overused list) and flagged my em dashes. I landed on Young Serif and rewrote the copy.

What I learned

  • A QR code is mostly an error-correction problem, not a drawing problem. At level H about 30% of the code can be covered and still decode, so "embed a logo" really means "raise the error-correction level." The app bumps to H on logo upload.
  • Masking is a scored search. You build the matrix under all 8 masks, score each with four penalty rules, and keep the lowest. The wrong mask gives you a valid code cameras struggle to lock onto.
  • The cheapest way to trust a generator is to write its inverse. Encode, read back, compare. It exercised the mask recovery, de-interleaving, and error-correction math all at once.
  • A consistency check on the error-correction table (block codewords summed against the standard totals) guards all 160 entries against a typo.

What carried forward

This was the third project in a row using the pattern from #9 and #10: one pure qr.js with no DOM, imported by both the browser and the Node tests, so the shipped code is the tested code. The same module also rendered sample PNGs from Node with nothing but zlib.

Two things from this folder outlived it. The serve.mjs here became the reference server: the house rules in 00-system/capstone-split.md point every builder to projects/011-qr-studio/serve.mjs. And #75 (Gated Admin Panel) imports qr.js directly, not a copy, to draw the QR codes for authenticator enrollment. #61 then imports that QR helper from #75, so this encoder ended up two projects deep.

If I took it further

I'd add alphanumeric and numeric modes to shrink the grid, and a proper Wi-Fi and contact form builder instead of raw payload strings. Rounded finder eyes and gradient fills would give more brand styling without hurting the scan.

#011 Built with Claude Code June 23, 2026 390 automated checks

Want one like this?