Thanks to visit codestin.com
Credit goes to Github.com

Skip to content

"cue-are-bitter" (qrbitr): QRBitr is a lightweight tool that lets you send bits of data - text, links, or files - over the air using QR codes. No cloud, no setup, just scan.

License

Notifications You must be signed in to change notification settings

austinwin/qrbitr

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

QRbitr – Quick, Private, Offline Data Transfer in the Browser and Client Side Only

Buy Me A Coffee
QRbitr App: https://austinwin.github.io/qrbitr/

Latest Release Top Language Last Commit Contributors Stars View Beelyt License

QRbitr is a browser-only application for sending and receiving small files or text via a rapid sequence of QR codes.
It’s private by design - no one, including IT admins or network monitors, can see your transfer because nothing ever leaves your devices.
No setup, no account, no Bluetooth pairing, no cables - just open it in a browser and transfer.

Perfect for:

  • Moving data between a work laptop and a personal phone without triggering network logging.
  • Sharing files when you have no internet, no USB cable, or no permission to install software; just pwa.
  • Quick one-off transfers without signing into cloud services.
  • Large text files — automatically compressed before sending and decompressed on the receiver's side for faster, more efficient transfer.

Because QRbitr runs entirely in your browser and operates offline, it leaves no server trace, requires no pairing, and is as simple as pointing a camera at a screen either through the online web app or pwa.


Features

  • Offline file & text transfer (client-side only, no servers).
  • Privacy-first – nothing is sent over a network.
  • Fountain coding (LT codes) with robust soliton distribution for resilience to frame drops.
  • Peeling decoder with Gaussian elimination fallback for complete recovery.
  • Automatic compression (Deflate via pako) when beneficial — great for large text files.
  • Real-time QR generation at configurable FPS.
  • Adaptive redundancy (extra fountain frames) to improve success in noisy conditions.
  • CRC-32 integrity checks for final verification.
  • On-screen debug logs – easily view detailed transfer logs directly in the browser (works even on mobile).
  • Works on modern desktop & mobile browsers, installable as a PWA.

About the protocol

QRbitr implements a one-way, high-redundancy broadcast protocol over visual QR frames.
It uses LT (Luby Transform) fountain codes to generate a stream of QR frames where each frame carries either original data chunks or XOR combinations.
The receiver collects any sufficient subset of frames to reconstruct the payload — no need for every frame to arrive.
This is ideal for environments where frame loss is common, like camera scans of rapidly changing QR codes.


How it works (high level)

flowchart TD
  subgraph Sender [Sender]
    A[File/Text Input] --> B[Chunking]
    B --> C[Optional Compression Deflate]
    C --> D[Metadata Frame]
    C --> E[Base Chunks]
    E --> F[LT Encoder - robust soliton]
    D --> G[QR Frame Builder]
    F --> G
    G --> H[QR Renderer @ FPS]
  end

  subgraph Receiver [Receiver]
    I[Camera] --> J[jsQR Decoder]
    J --> K[Frame Parser]
    K --> L[Chunk Store + Index]
    L --> M[Peeling Decoder]
    M -->|stalled| N[Gaussian Elimination]
    M -->|recovered| O[Reassembly]
    N --> O
    O --> P[Decompression if used]
    P --> Q[CRC-32 Verify & Download]
  end
Loading

Pipeline summary

  1. Sender splits data into fixed-size chunks and creates a metadata frame.
  2. LT encoder emits a mix of original and fountain (XOR) chunks following a robust soliton distribution.
  3. Each chunk becomes a QR frame; frames cycle at the configured FPS.
  4. Receiver scans frames; peeling + (if needed) Gaussian elimination reconstruct missing chunks.
  5. Payload is reassembled, decompressed (if used), CRC-checked, and saved.

Algorithms & references


Project structure

  • index.html – App UI and wiring.
  • qr-stream.js – Send/receive logic, frame creation, progress, events.
  • lt-codes.js – Fountain code encoder/decoder, soliton distribution.
  • prng.js – Pseudo-random number generator for chunk selection.
  • utils.js – CRC-32, Base64 helpers, formatting, decompression.
  • jsQR.js – QR scanning/decoding from camera frames.
  • qrcode.min.js – QR generation.
  • pako.min.js – Deflate/inflate compression.

Quick start

  1. Open index.html in a modern browser (desktop recommended for sending).
  2. Send mode: pick a file or paste text → set FPS/chunk size/redundancy → Start.
  3. Receive mode: allow camera access → point at sender’s screen → wait for progress → file auto-downloads.

No build required. Can be hosted as static files for easy sharing.


Configuration (main knobs)

Param Default What it controls
chunkSize 800 bytes Data bytes per QR frame before encoding.
redundancy 0.5 Ratio of extra fountain frames for resilience.
fps 20 QR frames per second displayed.
maxFileSize 10 MB Safety cap for payload size.
solitonC 0.03 Robust soliton distribution parameter.
solitonDelta 0.05 Robust soliton distribution parameter.

Performance tips

  • Keep chunkSize moderate (600–900 bytes) to balance frame count vs QR size.
  • Stay ≤ 10 FPS unless your receiver can decode faster.
  • Increase redundancy for poor lighting or shaky scanning.
  • Use a bright screen and steady positioning for best results.

Browser support

  • Modern Chrome, Edge, Firefox.
  • iOS Safari works but may have lower camera FPS.

Protocol Usage

Minimal Example

import { QRStream } from './lib/qr-stream.js';

// Sending
const sender = new QRStream({
  debugCallback: console.log,
  statusCallback: console.log
});
const fileInput = document.querySelector('#fileInput');
const sendCanvas = document.querySelector('#sendCanvas');
fileInput.onchange = () => {
  sender.startSending(fileInput.files[0], sendCanvas);
};

// Receiving
const receiver = new QRStream({
  debugCallback: console.log,
  statusCallback: console.log,
  resultCallback: html => { document.getElementById('result').innerHTML = html; }
});
const video = document.querySelector('#video');
const recvCanvas = document.querySelector('#recvCanvas');
document.querySelector('#startReceive').onclick = () => {
  receiver.startReceiving(video, recvCanvas);
};

Usage Breakdown

Sending

  1. Create a QRStream instance with desired config and callbacks.
  2. Call startSending(file, canvas):
    • file: a File object (from <input type="file">).
    • canvas: a <canvas> element to render QR codes.
  3. QR codes will be displayed on the canvas in a loop for scanning.

Receiving

  1. Create a QRStream instance with desired config and callbacks.
  2. Call startReceiving(video, canvas):
    • video: a <video> element (camera preview).
    • canvas: a <canvas> element (for internal decoding, not shown to user).
  3. Grant camera access and point at sender's QR codes.
  4. When transfer completes, the result is provided via the resultCallback.

Note: All transfer logic is browser-only, no server required.

More examples

Below are compact examples showing common usage patterns and advanced operations.

1) Minimal sender + receiver HTML (complete example)

Use this as a starting point in a single page — pick a file to send, then point the receiver camera at the sender canvas.

<!-- Minimal app: send + receive -->
<input id="fileInput" type="file" />
<button id="startSend">Start Send</button>
<canvas id="sendCanvas" width="512" height="512"></canvas>

<video id="video" autoplay playsinline style="width:240px;height:180px;"></video>
<canvas id="recvCanvas" style="display:none;"></canvas>
<button id="startReceive">Start Receive</button>

<div id="status"></div>
<div id="progress"></div>
<div id="result"></div>

<script type="module">
  import { QRStream } from './lib/qr-stream.js';

  // Sender
  const sendCanvas = document.getElementById('sendCanvas');
  const fileInput = document.getElementById('fileInput');
  const sender = new QRStream({
    fps: 10,
    redundancy: 0.6,
    debugCallback: msg => console.log('[SEND]', msg),
    statusCallback: s => document.getElementById('status').innerText = s
  });
  document.getElementById('startSend').onclick = () => {
    if (!fileInput.files[0]) return alert('Pick a file first');
    sender.startSending(fileInput.files[0], sendCanvas);
  };

  // Receiver
  const video = document.getElementById('video');
  const recvCanvas = document.getElementById('recvCanvas');
  const receiver = new QRStream({
    debugCallback: msg => console.log('[RECV]', msg),
    statusCallback: s => document.getElementById('status').innerText = s,
    progressCallback: p => document.getElementById('progress').innerText = p + '%',
    resultCallback: html => document.getElementById('result').innerHTML = html
  });
  document.getElementById('startReceive').onclick = () => {
    receiver.startReceiving(video, recvCanvas);
  };
</script>

2) Programmatic control: pause/resume & restart from percentage

You can stop the sender loop and restart from a percentage location in the prepared frames.

// Stop sending temporarily
sender.stopSending();

// Resume sending from beginning
sender.restartSending(sendCanvas, 0);

// Resume sending from 25% into the data frames
sender.restartSending(sendCanvas, 25);

// Note: restartSending expects the same canvas and that startSending was called earlier

3) Send a short "trailer" burst to signal completion

To explicitly mark the end of the transfer you can display the trailer frames. This is useful for receivers to trigger final decoding attempts.

// Show trailer burst (pauses normal loop briefly)
sender.sendTrailerFrames();

4) Advanced receive options: disable peeling or gaussian fallback

Tune decoding strategy when initializing the receiver.

// enablePeeling = false to skip peeling decoder
// enableGaussian = true to allow gaussian elimination fallback
receiver.startReceiving(video, recvCanvas, false, true);

5) Tuning knobs (what to change)

  • fps: frames-per-second the sender renders (lower helps unreliable cameras).
  • redundancy: fraction of extra fountain chunks (0.5 = 50% extra).
  • chunkSize: bytes per chunk (600–900 recommended).
  • solitonC / solitonDelta: LT code distribution parameters (advanced).

Example config:

const senderFast = new QRStream({ fps: 15, redundancy: 0.75, chunkSize: 800 });
const receiverSlow = new QRStream({ debugCallback: console.log });

License

MIT (see LICENSE).


Sponsor

Buy Me A Coffee

About

"cue-are-bitter" (qrbitr): QRBitr is a lightweight tool that lets you send bits of data - text, links, or files - over the air using QR codes. No cloud, no setup, just scan.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •