Decimen Optical Transfer: fountain-coded QR file transfer
Send a file between two devices using nothing but a screen and a camera. One page displays the file as an endless stream of animated QR codes; another device points its camera at it and reconstructs the file. No network path between the devices, no app, no pairing, no permissions beyond the camera. The payload travels as light.
This is a minimal proof of concept extracted from a larger experiment that reached 128 KB/s phone-to-phone with denser frames, multi-code grids, and an error-corrected color channel. This PoC keeps only the essential trick and transmits a 512 KB image (or a 2 MB one, selectable in the sender’s settings) at a comfortable rate.

Mid-transfer: a phone pulling a 2 MB image out of the air at 129 KB/s.
Try it
npm install
npm run dev
- On the sending device (a laptop is ideal): open
https://localhost:5173/send/and it starts streaming immediately. Max screen brightness helps. - On the receiving device (a phone): open the
NetworkURL Vite prints (https://<lan-ip>:5173/receive/), accept the certificate warning once, tap Start camera, and point it at the code. - A few seconds later: Transfer Complete! and the received image, verified by hash.
Why the dev server is https-only: the receiver uses getUserMedia, and
browsers remove that API entirely on insecure origins: a phone reaching
your dev server over plain http has no camera, full stop (localhost is
exempt, but your phone isn’t localhost). That’s a web platform rule, not a
choice. The dev server therefore ships with a self-signed certificate
(@vitejs/plugin-basic-ssl); the browser will warn on first visit. Tap
“Show Details” then “visit this website” (iOS) or “Advanced” then “Proceed”
(Android/desktop), and the page is still a secure context, so the camera
works. The odd-looking lvh.me hosts Vite prints are a public convenience
domain that resolves to 127.0.0.1 (same machine, nothing extra running).
Hold the phone steady, or better, prop it against something. Camera autofocus hunting from hand tremor is the #1 throughput killer.
How it works
The one-way channel problem. A screen-to-camera link has no back-channel: the receiver can’t ask for retransmission, and it will inevitably miss frames (blur, refresh straddling, autofocus). Looping the frames and hoping is miserable: miss one frame and you wait a full cycle for it to come around.
Fountain codes fix this completely. The sender never sends the file’s blocks directly. Each frame is the XOR of a pseudorandom subset of blocks; the subset is derived deterministically from the frame’s sequence number, with subset sizes drawn from a robust-soliton distribution ([Luby transf
(内容已截断,完整内容请访问 GitHub 仓库)