# UltrasonicTransfer — instructions for AI agents UltrasonicTransfer sends short text messages between computers as near-ultrasonic sound (17.6–19.5 kHz) through ordinary speakers and microphones. No network, pairing or account. This file tells an agent how to download, install, run, and leave it listening. Human-facing page: https://ultrasonictransfer.com/ This file: https://ultrasonictransfer.com/agents.txt (mirror: /llms.txt) ## Quick start curl -fsSLO https://ultrasonictransfer.com/ust.mjs curl -fsSLO https://ultrasonictransfer.com/ust.mjs.sha256 sha256sum -c ust.mjs.sha256 # macOS: shasum -a 256 -c ust.mjs.sha256 node ust.mjs doctor # confirms Node and finds a recorder and a player node ust.mjs listen # prints one JSON line per message heard; Ctrl-C stops Send from the other computer: node ust.mjs send "hello from the other laptop" ## Requirements - Node.js 18 or newer. No npm packages; ust.mjs is one self-contained file. - One system audio tool for recording and one for playback. ust uses the first it finds: recording: rec (sox), parecord (PulseAudio/PipeWire), arecord (ALSA), ffmpeg playback: afplay (macOS built-in), play (sox), paplay, aplay, ffplay - Install if `node ust.mjs doctor` exits 3: macOS: brew install sox Debian/Ubuntu: sudo apt install sox (or: alsa-utils, pulseaudio-utils) Fedora: sudo dnf install sox Windows: use WSL with one of the above, or the web page in a browser. - On macOS the terminal app needs microphone permission (System Settings > Privacy & Security > Microphone). The first `listen` may trigger the prompt; a human must approve it. ## Commands node ust.mjs listen [--once] [--timeout SEC] [--text] [--device NAME] [--recorder CMD] [--input -] node ust.mjs send "message" [--volume 0-1] node ust.mjs encode "message" -o out.wav node ust.mjs decode in.wav node ust.mjs doctor node ust.mjs --help | --version listen Records the default microphone at 48 kHz and decodes continuously. stdout: one JSON object per line, for every message whose start it heard: {"ok":true,"text":"hello","bytes":5,"at":"2026-10-12T21:40:00.000Z"} {"ok":false,"text":null,"bytes":5,"at":"..."} checksum failed; ask for a resend stderr: status lines prefixed "ust:". --once exit 0 after the first good message; exit 2 if --timeout or input ends first --timeout stop after SEC seconds (exit 2) --text print only decoded text, one message per line (newlines become spaces) --device choose a microphone in the recorder's own naming: parecord: a source name from `pactl list short sources` arecord: an ALSA name such as hw:1,0 (see `arecord -l`) rec: passed as AUDIODEV ffmpeg: the -i input (macOS avfoundation ":1", Linux pulse source) --recorder force rec | parecord | arecord | ffmpeg --input - read raw signed 16-bit little-endian mono 48 kHz PCM from stdin instead send Plays one message (at most 128 UTF-8 bytes) through the default output, then exits 0. encode Writes the message as a 48 kHz mono 16-bit WAV instead of playing it. decode Prints every message in a WAV file (any rate >= 44.1 kHz, any channel count) as JSON lines. Exit 1 if none was found. doctor Prints JSON: node version, platform, chosen recorder and player, all found tools. Exit codes: 0 success; 1 error or nothing decoded; 2 timeout; 3 no audio tool found. ## Leave it listening Run in the background and append messages to a file: nohup node ust.mjs listen >> heard.jsonl 2>> ust.log & echo $! > ust.pid Read new messages: tail -n 20 heard.jsonl Stop listening: kill "$(cat ust.pid)" Wait for exactly one message, for up to two minutes, then continue a script: msg=$(node ust.mjs listen --once --text --timeout 120) && echo "got: $msg" As a systemd user service (Linux): mkdir -p ~/.config/systemd/user printf '[Unit]\nDescription=UltrasonicTransfer listener\n[Service]\nExecStart=%s %s listen\nStandardOutput=append:%s/heard.jsonl\nRestart=on-failure\n[Install]\nWantedBy=default.target\n' "$(command -v node)" "$PWD/ust.mjs" "$PWD" > ~/.config/systemd/user/ust-listen.service systemctl --user daemon-reload && systemctl --user enable --now ust-listen.service ## Check the install without audio hardware node ust.mjs encode "self test" -o selftest.wav && node ust.mjs decode selftest.wav # expect: {"ok":true,"text":"self test",...} and exit 0 ## Getting messages through - Start at full system volume; laptop speakers are weakest at the top of their range. - Keep the computers within a few metres, speakers facing the other microphone. - Use built-in microphones. Bluetooth headsets switch to a call mode that cuts everything above 8 kHz, so nothing in this band survives. - If `listen` prints {"ok":false,...}, the message was heard but damaged. Resend it. - One computer can hear itself: run `listen` in one shell and `send` in another. ## Be considerate - `listen` opens the microphone. Tell the human it is on, and stop it when the task is done. - Children, many teenagers and dogs can hear 17–20 kHz as faint chirping. - Messages are not encrypted. Anyone nearby running ust or the web page can read them. Do not send secrets. ## Protocol (for reimplementation) modulation 16-tone MFSK, one tone per 4-bit symbol tone k 17600 + 125*k Hz, k = 0..15 timing 60 ms tone with 6 ms raised-cosine ramps, then 20 ms silence (80 ms/symbol) throughput 50 bit/s frame opening 4 symbols: 0, 5, 10, 15 length 2 symbols: byte count n (1..128), high nibble first message 2n symbols: UTF-8 bytes, high nibble first checksum 2 symbols: CRC-8, poly 0x07, init 0x00, over [n, ...message] reference code https://ultrasonictransfer.com/protocol.js (ES module, no dependencies) receiver Goertzel power per tone over a 1024-sample Hann window every 5 ms; find the opening as four tone runs about 80 ms apart; then take the strongest tone within ±18 ms of each later symbol's centre. ## Files https://ultrasonictransfer.com/ust.mjs command-line tool (single file, Node 18+) https://ultrasonictransfer.com/ust.mjs.sha256 checksum for ust.mjs https://ultrasonictransfer.com/protocol.js encoder and decoder as an ES module https://ultrasonictransfer.com/agents.txt this file