QR Scanner decodes the data inside a QR code image without sending anything to a server. The decoding work is done by the jsqr library, a pure JavaScript port of the ZXing reader that walks the pixel grid, locates the three position markers in the corners, samples the data modules, and reconstructs the original payload using Reed-Solomon error correction. Everything runs on your device's CPU inside the browser tab.
There are three ways to feed a code into the scanner. The camera path uses MediaDevices.getUserMedia with facingMode: 'environment' so phones default to the rear lens; the live video frames are drawn onto a hidden Canvas API element each animation frame, then the raw ImageData buffer is handed to jsqr. The upload path uses FileReader.readAsDataURL to load JPG, PNG, or WebP files into an Image, paint it onto the canvas, and decode the same way. The clipboard path listens for the document paste event and pulls image blobs out of ClipboardEvent.clipboardData.
Camera scanning needs explicit user permission. The first time you click Start Camera, the browser shows a native permission prompt; if you deny it, the only recovery is to reopen site permissions in your browser settings. The page must also be served over HTTPS for getUserMedia to be exposed at all — that is a hard requirement of the modern web platform, not a tool choice.
Decoding speed is bounded by image size and lighting. jsqr inspects every pixel, so a 4K phone screenshot takes longer than a 720p video frame. Poor focus, low light, glare, or a tilted angle of more than about 30 degrees cause the position markers to fall below the detection threshold and decoding silently fails. Steady the phone, fill more of the frame with the code, and avoid reflective laminates.
Payload parsing happens after decoding. The tool inspects the prefix of the decoded string and routes it to a specialised renderer: http:// or https:// becomes a URL with an Open button, mailto:, tel:, and sms: are stripped to their raw value, WIFI:T:WPA;S:Network;P:pass;; is split into SSID, password, and security fields, and a BEGIN:VCARD block is parsed into name, phone, email, organisation, and URL plus a Save Contact button that builds a .vcf Blob via URL.createObjectURL. geo: links and plain text fall through as-is.
Mobile and desktop behaviour diverges in two places. Phones expose a rear camera with autofocus and often a torch — when track.getCapabilities() reports torch: true, a flashlight button appears and toggles the LED through track.applyConstraints. Laptops typically only have a front-facing camera with no torch, so the rear-camera request silently falls back to facingMode-agnostic capture and the torch button never appears.
Nothing leaves your tab. Scan history is held in component state, exporting it produces a CSV via a client-side Blob, and stopping the camera releases every MediaStreamTrack so the recording indicator in your OS disappears immediately. Closing the tab discards the history; this tool has no backend to persist it to.