lumen.
encode & broadcast
Type a short message. The encoder emits a preamble pair, then four payload frames carrying 32 bits each (16 patches × 2 bits via differential 4-PSK on a low-frequency DCT basis).
visible carrier
This is what your camera (or a second device's camera) sees. Point a phone here and switch to receive.
open camera
Allow camera access. Point at a screen running the transmit panel. Keep the framed area filling most of the view; hold steady; even, indirect lighting works best.
recovered bytes
Once the preamble locks, the decoder samples each patch's average luminance, takes a frame-to-frame difference, projects onto the horizontal/vertical DCT basis, and recovers two bits per patch.
characterize the channel
Just like the audio self-test that swept 1/5/10/15/19 kHz tones to find what your microphone actually heard, this sweeps known luminance steps and known spatial DCT patterns to see what your camera actually captures.
what the camera saw
Open camera and point at the calibration panel. Each tile reports the measured response to that test pattern.
The acoustic analogy.
An acoustic modem maps bits onto orthogonal sinusoids in time. This optical modem maps bits onto orthogonal 2D-DCT basis functions in space, modulated differentially across frames in time. The channel has more dimensions, so the carrier alphabet is richer.
Symbol design.
The screen is tiled into a 4×4 grid of patches. Each patch carries 2 bits. The bits choose one of four states: (±1) × (horizontal stripe) or (±1) × (vertical stripe), where the stripe is a 2-cycle DCT basis function. Sign and orientation give 4 PSK-like states.
Why differential.
Cameras have unknown gamma, white balance, exposure. Encoding the change from frame to frame is robust to these unknowns the same way DPSK in audio is robust to phase offset. Each payload frame is interpreted relative to the frame before it.
Preamble lock.
The transmitter starts every burst with a checkerboard frame followed by its inverse. The receiver cross-correlates that known checkerboard-difference against incoming frame-deltas; when correlation peaks, frame timing and patch grid alignment are both established at once. This is the optical version of the matched-filter preamble that worked in the audio modem.
Frame timing.
The camera frame rate is almost never an integer multiple of the screen rate, and rolling shutter samples rows at slightly different times. The receiver oversamples (it grabs every camera frame) and uses the preamble correlation to align to the correct sender frame, dropping camera frames that fall mid-transition.
Realistic rates.
At 10 fps × 32 bits = 320 bps raw, before error correction. In practice, expect to lose half to retries and frame drops. The acoustic modem hit similar territory at the ultrasonic edge — 20 to 200 bps is honest territory for a self-contained browser implementation. Neural decoders in the literature push much higher.
What's not here.
No FEC (a Reed-Solomon outer code would help), no multi-color carriers (chrominance is its own orthogonal channel), no perspective correction (the receiver assumes the sender fills most of the frame, roughly square). All worthwhile next steps if the basic loop closes.