I had an API endpoint that returned a QR code as a Base64 image, and I needed to validate it — to confirm the thing actually encoded the payload I thought it did, not some stale or malformed value.
The workflow I wanted was trivial in theory: take the Base64 the API gave me, decode the QR, read the text back, compare. In practice I was bouncing between an online Base64-to-image converter, a screenshot, and my phone's camera pointed at the monitor. Not a good look.
So I built the QR Code Encoder/Decoder on spanners.dev.
What it does
Two tabs, both entirely in the browser:
- Encode — paste any text and get a live QR code. Pick the error-correction level (L/M/Q/H), copy the PNG data URL as Base64, or download PNG/SVG.
- Decode — drop a screenshot, choose an image, paste one from the clipboard, or paste a Base64 value directly. It shows the source image and the decoded text.
That second tab is the whole reason it exists. Paste the Base64 from the API response, and the decoded payload appears next to it. No phone involved.
Under the hood
It's two dependency-free, pure-JS libraries glued together: qrcode-generator for encoding and jsqr for decoding pixels. Text is UTF-8 encoded before it goes in, so emoji and non-Latin scripts survive the round trip. The decode side sniffs the image type from the decoded header, so it accepts a full data:image/png;base64,… URL or a bare Base64 string, standard or URL-safe.
The logic lives in a small pure module with a test that rasterises an encoded matrix and decodes it back with jsQR — which is really just an automated version of what I used to do with a phone camera.
Nothing leaves your browser, which matters when the QR code is sitting in an API response you're debugging.