Skip to content

About

Phone-to-phone text over light: one screen flashes colours, the other phone’s camera reads them. No network, no pairing.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Distance Bits

Phone-to-phone text over light. One phone's screen flashes a grid of colours; the other phone's camera reads it. No Wi‑Fi, Bluetooth, cell signal or pairing: if you can see the other phone, you can text it.

It is an experiment, and it works, slowly. Messages decode reliably at a few metres indoors. Longer range is the open problem, and the reason this repo is public.

Status (October 2026)

iPhone app (app/, Flutter) Working: texting-style chats, encrypted chats per 3-character code, an Open channel, delivery confirmation by flashlight. Tested on an iPhone 12 Pro and 14 Pro.
Android Builds from the same code but untested. The camera exposure, brightness and torch controls are native iOS code with no Android version yet.
Web app (src/, TypeScript) The original prototype, kept as the reference protocol implementation and an in-browser simulator. It can send and receive, with no delivery confirmation and no encryption.

Measured so far:

  • Near (2×4 grid, four colours): reliable at roughly 1.5–3 m.
  • Mid (two cells, three colours): works, at shorter range than expected.
  • Far (whole screen, two colours): decoded at arm's length. It is slow by design: about 7.5 bits/s, so "Oh" takes about 30 s. A simulation built from that run (app/test/range_sim_test.dart) predicts several metres with the phone held still. Hand movement is the limit it points to; that is still to be checked in the field.

How it works

The sender loops the message as a fountain code: there is no "resend", the receiver just keeps watching until it has enough pieces. A walking black cell (or, in Far mode, a black frame between symbols) acts as the clock. The receiver finds the screen by what makes it unusual (its colour keeps changing, unlike lamps and windows), reads the cell colours, and reassembles the message. When it succeeds it blinks its flashlight, and the sender's front camera watches for that to stop sending.

  • PROTOCOL.md: the wire format and receiver, as implemented.
  • DESIGN.md: the original design reasoning, with prior art (some of it has since been superseded; PROTOCOL.md wins).
  • SECURITY.md and PRIVACY.md: what the chat codes protect against (and what they don't), and what the app stores.

Try it

iPhone: there's no App Store or TestFlight build yet. To build it yourself you need a Mac with Xcode, Flutter 3.32+, and an Apple developer account (a free one works for your own phones):

cd app
flutter pub get
open ios/Runner.xcworkspace   # set your signing team and a unique bundle id
flutter run --release         # with the phone connected

Using it: both people create a chat with the same 3-character code (agree it in person). One types and taps send, then holds the screen toward the other phone; the other taps Listen and points the camera at it. Turn the sender's brightness up. It works best with the phones steady and the screen the brightest thing in view.

Photosensitivity warning: the sending screen flashes rapidly, which can trigger seizures in people with photosensitive epilepsy.

Browser: the web prototype runs on any phone with a camera over HTTPS. See Web prototype below.

Repository layout

app/                 Flutter app (iOS; Android untested)
  lib/core/            protocol and vision, pure Dart (a port of src/core)
  lib/chat/            chat list, history and settings storage
  lib/ui/              screens: chats, chat, send, listen
  ios/Runner/          native camera/brightness/torch channels (AppDelegate.swift)
  test/                unit tests, golden vectors, real-frame fixtures, range simulation
src/core/            reference protocol in TypeScript (fountain code, framing, receiver, vision)
src/app/             web prototype: sender, receiver, simulator
test-data/           a real receive log used as a regression test
scripts/             Mac helpers: app.sh (test/build/install), smoke.mjs (headless web check)

Development

npm install && npm test          # TypeScript protocol tests
cd app && flutter test           # Dart tests (protocol, crypto, vision on real frames)

The Dart core is checked against vectors generated by the TypeScript core (app/test/golden.json). After any protocol change, regenerate them: GEN_GOLDEN=1 npx vitest run src/core/golden.gen.test.ts.

scripts/app.sh test|build|install runs the Flutter steps from a copy outside ~/Documents, because iCloud Drive adds file attributes that break code signing. install puts a release build on every iPhone connected by cable.

The range simulation prints a table of screen size × hand movement vs. decode success (about 5 minutes):

cd app && flutter test test/range_sim_test.dart --dart-define=RANGE_TABLE=1

Field testing

Turn on Settings › Save field-test diagnostics on the receiving phone. Each Listen session then saves a per-capture CSV log, periodic snapshots and short frame bursts to the app's folder (Files › On My iPhone › Distance Bits, or xcrun devicectl device copy from … --domain-type appDataContainer). Most fixes in the history were found by replaying these against the code. Note the snapshots are pictures of whatever the camera was pointed at.

Web prototype

npm run dev        # https://<your-computer-ip>:5173 (self-signed cert; accept it on each phone)
npm run build      # production build in dist/

Pushes to main publish the web app to GitHub Pages (.github/workflows/deploy.yml). The simulator page sweeps "screen pixels" to stand in for distance. npm run smoke runs it headless in Chromium (after npm run build && npm run preview -- --port 4173, and npx playwright install chromium once).

Contributing

Issues and pull requests are welcome, especially field logs from new distances, lighting and phones, and an Android port of the native camera controls. If you send logs, check the snapshots first: they are camera pictures.

License

GNU Affero General Public License v3.0. © 2026 Trevor Johnson.

About

Phone-to-phone text over light: one screen flashes colours, the other phone’s camera reads them. No network, no pairing.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages