QR Stream
Transfer files and text between devices by displaying an animated sequence of QR codes and reading it with a camera. No network, no Bluetooth, no cables, 100% private. Packed as both the web app (send & receive) and CLI (send only).
Live demo: https://230590.xyz/hermes-web-demos/qr-transfer/
- Web app works purely in browser, no server, no telemetry, 100% private
- Mobile friendly & browser agnostic
- 8mb file size limit (<100kb recommended)
- Preserves filename of files, instantly shows received text
- Applies compression in the background - typically saves 3x-10x bandwidth on text files
- outer Reed-Solomon redundancy & fountain codes for efficiency in case of dropped frames
- CRC32 checksums for consistency
- Save as gif, embed anywhere with gif support
Installation
npm
npm install -g qr-stream
Bun
bun install -g qr-stream
You can also run it directly without installing:
npx qr-stream [file]
bunx qr-stream [file]
CLI Usage
Encode text or a file into a looping QR sequence
# Read from file
qr-stream document.pdf
# Read from stdin
echo "Hello, world!" | qr-stream
# Pipe file contents
base64 image.png | qr-stream
The terminal clears, enters an alternate screen buffer, and displays the QR frames in a loop at 10 FPS. Press q or Ctrl-C to quit.
Start the web app preview server
qr-stream --serve start web app preview server
qr-stream --serve --port 8080 custom port
qr-stream --serve --host 127.0.0.1 localhost only
Serves the built web UI. Defaults to port 3000 and binds 0.0.0.0. Change the port with the --port flag or PORT environment variable:
PORT=8080 qr-stream --serve
The server resolves the dist/ directory automatically, so it works from both the bundled CLI and a local checkout.
CLI flags
| Flag | Description |
|---|---|
-h, --help |
Show usage information |
-s, --serve |
Start the web preview server |
--port <n> |
TCP port for --serve (default: 3000, also: PORT env) |
--host <ip> |
Bind address for --serve (default: 0.0.0.0) |
Development Setup
Prerequisites
Install dependencies
npm install
Start the dev server
npm run dev
Starts Vite with hot reload on http://localhost:5173.
Build
npm run build
Produces:
dist/index.htmlanddist/assets/*- the web appdist/qr-stream.js- the self-contained CLI bundle
Preview the production build
npm run preview
Serves the contents of dist/ locally exactly as it will run in production.
Run tests
npm test
Runs the full test suite (57 tests) via Vitest.
Run the CLI from source
bun run src/cli/qr-stream.ts
# or
npm run cli
Build the CLI only
npm run build:cli
How It Works
- Sender compresses your data, splits it into chunks, and wraps each chunk in a QR code.
- The QR codes are shown as an animated sequence (in the terminal or as a GIF in the browser).
- Receiver scans the sequence with a camera or uploads a GIF, decodes the frames, and reassembles the original file or text.
The protocol uses fountain coding (RLNC over GF(256)) so the transfer survives frame loss, glare, and partial obstructions without needing every single frame.
For a deep dive into the packet format, algorithms, and design decisions, see ARCHITECTURE.md.
License
MIT - built for fun and utility.