Breathing Sonar (Remote)

Put the phone where ApneaApp put it — on the nightstand, a metre from a chest — and keep the laptop as the screen. The phone becomes the sonar: it emits the inaudible signal, records its own echoes, and runs the whole pipeline on board, streaming only the results here over WebRTC. The physics is explained on the single-device page; this one is the same code with the front end moved.

Pair your phone

Code — — — —

Generating session…

On the phone, open sensingstudio.org/breathing-sonar-remote and enter this code. Both devices need to be online; the audio never leaves the phone.

Method

A single inaudible tone. The complex baseband echo is one vector; the room pins it in place and your chest rotates it. Most sensitive, but blind to distance.

Parameters · CW phase

18.0 kHz

Carrier frequency, 1–22 kHz. Higher → shorter wavelength → more phase per millimetre of chest movement (38°/mm at 18 kHz, 2°/mm at 1 kHz), but laptop tweeters and microphones both roll off hard above 20 kHz. If the echo looks dead in the signal panel, come down — and keep coming down. Below about 17 kHz you will hear it, which is unpleasant but diagnostic: an audible tone proves the speaker is actually emitting, and the 8–14 kHz region is where cheap hardware is happiest. Below ~2 kHz the wavelength is longer than a room feature and clutter cancellation has little left to separate.

6.0 s

Time constant of the running average that estimates the static part of the echo — walls, desk, the direct speaker-to-mic leak. Subtracting it re-centres the constellation on the origin so the breathing arc is what is left. Too short and it starts eating slow breaths.

0.25 s

Low-pass on the complex echo before the phase is taken. Breathing lives below 1 Hz, so heavy smoothing costs nothing and keeps the phase from random-walking when the echo is weak.

Run

Sit 30–80 cm in front of the laptop, chest facing it, then press Start and breathe normally.
0.35

Echo cancellation, noise suppression, and AGC are all switched off so the microphone reports the sonar honestly. Headphones will not work — the sound has to reach your chest through the air. Hold reasonably still: the same physics that sees a 1 mm breath sees a 1 cm fidget far more loudly. The Signal check panel below shows the raw microphone waveform and spectrum — start there if the results look like nonsense.

Why the phone does the maths

The obvious split — phone streams its microphone, laptop does the signal processing — cannot work here, and it is worth knowing why. A WebRTC audio track is Opus-coded and resampled through a jitter buffer. Opus is a perceptual codec: it preserves what you hear and discards phase relationships you do not, and the jitter buffer stretches and shrinks the stream by fractions of a sample to keep the clocks aligned. Both are harmless for speech and fatal here, because the entire measurement is the phase of an 18 kHz carrier, and 1 mm of chest movement is 38° of it.

So the phone keeps the samples. It plays the tone or the sweep, captures its own microphone at the native rate, and runs the identical sonar-core.js that the single-device page runs. What crosses the network is the finished picture: a few hundred numbers per packet — the motion trace, the breathing spectrum, the range profile, the envelope, and enough of the raw waveform and spectrum to fill the signal-check panel. About 40 kB/s, four packets a second.

The capture-rate readout in the signal panel is the phone's, not the laptop's: it counts the samples the phone's DSP actually consumed. If it sags below the sample rate, the phone is the bottleneck — close its other apps, or drop the chirps-per-frame.

The controls above are commands, not settings: change a parameter here and the phone re-initialises its DSP and answers with new packets. That is also why the layout is identical to the single-device page — both pages call the same drawing code over the same render buffers, so what you see here is what the phone computed, not an approximation of it.

Placing the phone

  • Speaker and microphone both face the chest. Flat on a nightstand pointing up is worse than propped against something at torso height.
  • Thirty to eighty centimetres. ApneaApp claims a metre; past that the echo is buried, and with a 512-sample sweep the far half of each chirp is wrapped anyway.
  • Plug the phone in. Half an hour of continuous 19 kHz output and full-rate DSP is not a light load.
  • Check the signal panel before trusting anything. Phone speakers vary enormously above 18 kHz; if the hump is missing from the received spectrum, drop the band.
  • A phone screen that sleeps may throttle its audio thread. Keep the screen awake for a long run.

Sonar endpoint

Enter the code shown on the laptop to turn this phone into the sonar.

Join a session