v0.1 · MIT · BC-UR receiver

Point a camera at an animated QR. Get the bytes.

A single QR caps out around 3 KB. Anything larger is split into an animated, fountain-coded sequence a camera can rebuild even while missing frames. Senders are everywhere; the browser lacked a good receiving end. This is that end: a pure decode core, pluggable frame sources, and a drop-in <ur-scanner> element.

$ npm i @blocco/ur-scanner @ngraveio/bc-ur

One QR is ~3 KB

QR is a zero-infrastructure transport: no pairing, no network, works across an air gap and in a power cut. But a single code is tiny, so bigger payloads must animate.

Fountain-coded, loss-tolerant

BC-UR fountain-codes the sequence: the receiver rebuilds the whole payload from any set of frames a bit larger than the original, and can join the stream at any point. Miss frame 7? It doesn't matter.

The receiving end

Every hardware wallet ships a sender; the web had no good, framework-agnostic receiver. This is not a wallet library, it hands you bytes. Wallet payloads are one recipe among many.

Drop-in web component

Import the browser-only subpath, add the tag, listen for ur-complete. Ten lines, any framework, or none.

Prefer to own the loop? The headless URReceiver core takes strings from a camera, a file, or a test fixture and is safe to import in SSR bundles.

index.html
<ur-scanner auto-start expected-type="bytes"></ur-scanner>

<script type="module">
  import '@blocco/ur-scanner/element';
  const scanner = document.querySelector('ur-scanner');
  scanner.addEventListener('ur-complete', (e) => {
    // e.detail.cbor is a Uint8Array; payloads are just bytes.
    console.log(`received ${e.detail.cbor.length} bytes`);
    scanner.stop();
  });
</script>

All processing is local. No telemetry.

No frame, byte, or pixel ever leaves the page. There is no network call of any kind. The camera stream goes to a canvas, the canvas to a QR detector, the strings to a decoder, all inside your tab. The library opens the camera only when you call start() and releases it on stop(). Even the fonts on this page are self-hosted.

Read the security policy →

Browser support

The core decode logic runs anywhere JavaScript does. Camera and detection depend on the browser. Native BarcodeDetector is fastest; where it's missing, install jsqr and it loads lazily.

BrowserQR detectionVerified
Chrome / Edge (desktop + Android)native BarcodeDetectoryes (Playwright/Chromium)
Safari (macOS 14+, iOS 17+)jsqr fallbackhelp wanted
Firefoxjsqr fallbackhelp wanted

The honest, per-device matrix (a community-PR target) lives in the compatibility docs.

See the bytes transfer

Open the demo on a laptop and your phone, point one at the other, and watch the ring fill. No second device? Fixture mode runs the whole pipeline on one screen.

ur-scanner Browser receiver for animated BC-UR QR codes. Camera to bytes, no wallet required.