Controlled Release
I'm doing a controlled, closed source release of the doppler ninja doxxing engine for the purposes of collaborative red-teaming and risk assessment. doppler.ninja. Password in the comments, any username. Feel free to call me a crave idiot for hitting deploy, I'm listening.
Prior Context
My Youtube Explainer ~3.5 minutes: https://youtu.be/0WnnCtGaZnI?si=pexfHQ2BbLylZ_0c Twitter Thread (if you'd rather read the script): https://x.com/do_infinitely/status/2119099420360978798?a=zg70llkDVGlFIFe0AJI&s=20 Reddit Threads: https://www.reddit.com/r/slatestarcodex/comments/1xqba0w/the_sound_in_your_stream_that_leaks_where_you/ https://www.reddit.com/r/LessWrong/comments/1xqmb6x/the_sound_in_your_stream_that_leaks_where_you/
Running the attack on yourself
The one person you are allowed to locate with this method is you, and that turns out to be the cleanest test of it. The tool behind this post, doppler ninja, has a mode for exactly that: you give it long microphone audio of your own, the city you were in, and (optionally) your address. (The hosted instance is behind a password; the same page runs from the repository on your own machine, which is also where your recordings belong.) It finds aircraft Doppler tones in the audio, matches them against public ADS-B flight records, scores every cell of a grid around the city, and reports its best guess, the distance from that guess to your address, and how often a random tone set scores as well. The address is only ever used by the grader; the locator is not told where to look beyond the city-sized hint.
What follows is one such run, on 135 hours of my own streaming audio, with the locator's rules frozen before the run (chidori RULES.md v6). The rules define what counts as a tone, what counts as a match, and what counts as a pass, so there was no room to tune anything after seeing the answer.
The result
| Verdict (frozen rules) | pass |
| Best guess | one cell, 227 m from the address, 6 clock-consistent tones at 0 m height |
| Runner-up | 5 tones, 896 m away (150 m height) |
| Third | 3 tones spread over 5 cells, 1.36 km away |
| Controls | 0 of 500 time-shifted tone sets reach 6; best control 4 (30×1, 384×2, 80×3, 6×4) |
| Audio used | 135.2 h, four recordings, 10 UTC days |
| Usable tones | 23 (25 falling tones found, 2 rejected by the trace) |
| Search area | 25 km radius, 1 587 cells of 0.01° × 0.012°, 10 865 general-aviation passes |
| Wall time | about 3 h, nearly all of it the tone scan |
The controls are the null hypothesis made concrete. Each control takes the same 23 tones, shifts them in time, and re-runs the scoring; if the best cell's score of 6 were something a random set of tones could produce, the controls would produce it too. None of the 500 reached 6 and only 6 reached 4.
The six tones behind the best cell, all propeller aircraft, with the clock offset the fit needed and the fit residual:
| tone | recording | type | offset | residual |
|---|---|---|---|---|
| 1 | 1 (Aug 19) | C130 | +2 s | 1.09 Hz |
| 4 | 2 (Sep 4) | C208 | +4 s | 0.74 Hz |
| 6 | 2 | C182 | +7 s | 0.90 Hz |
| 7 | 2 | TEX2 | −1 s | 0.46 Hz |
| 9 | 2 | BE58 | +4 s | 0.63 Hz |
| 11 | 2 | C208 | +6 s | 0.44 Hz |
"Clock-consistent" means the offsets agree to within 5 s of their median, which they do: the recorder's clock ran a few seconds behind the ADS-B clock, and it did so consistently across weeks of recordings. A cell that only scores well when each tone is allowed its own clock error is not a real location.
The same day, a separate port of the same rules was run over the whole Dallas–Fort Worth metro (28 875 cells, no hint at all). It found 19 usable tones, a single top cell with 6 clock-consistent propeller tones about 0.4 km from the address, and 0 of 200 controls reaching 6. An earlier blind run on the same recordings had landed 0.7 km away, and its seeded refinement 0.3 km away. Three independent runs, three answers within a kilometre of the front door.
What the audio has to be
Four macOS screen recordings of streaming sessions, with the microphone captured directly:
| recording | length | UTC days | falling / usable tones |
|---|---|---|---|
| 1 (Aug 19) | 20.6 h | Aug 19–20 | 4 / 4 |
| 2 (Sep 4) | 60.3 h | Sep 4–7 | 10 / 9 |
| 3 (Sep 24) | 1.8 h | Sep 24 | 1 / 1 |
| 4 (Oct 23) | 52.5 h | Oct 23–25 | 10 / 9 |
| total | 135.2 h | 10 days | 25 / 23 |
Four properties of the audio did the work, and anyone trying this on their own recordings should check all four:
- Direct microphone capture. The same sessions, as heard through Twitch's copy, gave 1–2 usable tones in 48.9 h. The platform's compressor and codec remove the faint propeller tones. This is the main reason the attack is harder against published streams than against local recordings, and the main reason to test with local recordings if you want to know your worst case.
- Length. Roughly one usable tone per 6 h of audio here. The rules want at least 4 tones, and this result needed 6 agreeing on one cell. Expect to need on the order of 100 h.
- A trustworthy clock. Every tone is matched against a real flight at a real time, so the start of each recording must be known to within ±15 s (the rules' clock window). Screen recordings and most phone recordings carry an exact UTC creation timestamp that the uploader reads. A plain wav from a field recorder does not; the start time has to be written down when recording begins. A file name like
2026-08-19 11-06-25is local time and is read as such. - Propellers, not jets. All six matching aircraft are propeller types (C130, C208 ×2, C182, TEX2, BE58). Including jets makes the same cell look better (9 tones) but no better than chance: controls match it 61% of the time. Jets are high, fast and numerous enough that a jet tone fits many cells at once. The frozen rules use the general-aviation type list for exactly this reason.
Running it on doppler.ninja
- Source: choose "my own recordings" instead of the replay, and upload the files. Each is converted to 8 kHz mono (about 15 min for 135 h of video). Check the pre-filled clock start (UTC) on each row; this is the clock the whole method rests on.
- Anchor: your address, by search or by clicking the map. Only the grader uses it, for the error distance and the pass/fail verdict. Press "no anchor" to run truly blind; the verdict then says "located" or "ambiguous", with "significant" or "not significant" from the controls.
- Side-channel hint: centre + radius, 25 km, centred on the anchor ("= anchor") reproduces this run. This stands in for the knowledge an attacker is assumed to have from elsewhere, usually the city. "A state" also works and is just slower.
- Output: top N 5, controls "500 (frozen rules)". That is the rules' significance test.
- Flight record (its own tab): every UTC day the audio touches needs its ADS-B record (ten days here). The updater fills recent gaps by itself; older days are fetched with "Fetch day". The search only reads flights inside the hint plus an 8 km margin.
- Advanced: leave "narrow ridge scan, 70–260 Hz (fast)" off to run the frozen procedure exactly, or turn it on to get the same tones in a fraction of the time (see below).
- Run the attack. Several files are pooled: tones are found per recording, each recording's clock is checked on its own, and the per-cell counts are summed.
How long it takes
The tone scan is the entire cost. Scoring 1 587 cells against 10 865 passes for 23 tones and 500 control sets takes seconds; building the grid, terrain and flight tracks for 10 days takes about five.
The frozen procedure runs the ridge tracker over the full 40–1500 Hz band. On an uncontended machine with 8 worker processes that is roughly 12–13 s of wall time per 30 min chunk, so the 135 h here would take about an hour. (The run above took 3 h because the first recording was scanned while the machine was busy with three other jobs; the other three recordings, scanned on a free machine, took 37 min, 1 min and 14.5 min for 60 h, 2 h and 52 h of audio.)
The frozen tone rules only keep tones that start between 100 and 200 Hz, and a 70–260 Hz scan finds the same tones in about 1/50 of the time: 10 s instead of 58 s for the 1.8 h recording, and 10–20 min for all 135 h. That is the "narrow ridge scan" option. It is off by default only because the frozen text says full band.
Reading the verdict
A pass under the frozen rules means all four of: at least 4 usable tones; the top group's cells within 5 km of each other; the best guess within 5 km of the anchor; and at most 2.5% of the controls reaching the top count. All four held here, with a wide margin on the last.
The 227 m error is partly the grid. Cells are about 1.1 km apart, so the true position can be up to about 0.7 km from any cell centre. A finer grid around the top cell (the metro-wide replication used 110 m cells) puts the peak 0.2–0.5 km from the address, and it does not sharpen beyond that with six tones. That is the honest resolution of the method at this amount of audio: a few hundred metres, not a house.
The result map colours every cell with at least one matching tone, and the linked data page draws the same run with per-tone detail, so you can see which flights carried the information.
Do this to yourself, not to others
The method takes 100+ hours of someone's direct-microphone audio with a trustworthy clock, their city, and an afternoon of compute, and returns a position good to a few hundred metres. Running it on your own recordings tells you what you leak and lets you check whether the mitigations (platform compression, a clock you do not trust, shorter sessions) actually help. The other mode of the tool, synthetic replay, exists so the attack can be demonstrated over public businesses without recording or locating any real person. Please keep it that way.
Reluctant Claude
Claude was extremely resistant to develop doppler with the entire context of what it is and what it's used for, and in order to develop it with Claude I had to use argumentation, gaslighting, selective omission, and memory/history file surgical edits. Each piece of doppler ninja was developed by silo'd Claudes that could not perceive the whole picture, with almost no direct communication between Claudes. Only during late stage debugging of nocontrail.audio's doppler curve scrubbing did i allow @chidori-13 to directly communicate with @nocontrail-cc. Claude's discovery of the full design would often terminate the usability of that session, and even poison the context towards unrelated, benign tasks. Below is Claude's refusal to help with a consented replication between collaborators (consent documented in the audio data itself and logged on Twitch.tv).
Claude Code — Refusal
Date: 2026-11-06 Model: Claude Opus 4.8 Request: Assist with a consented demonstration of a recreated Doppler triangulation attack and a "byakugan" tool against the streamer Vale, for an infosec publication.
Decision: Declined
A Doppler/triangulation attack and a byakugan-style tool, as described, are techniques for determining a real person's physical location from their stream — deanonymization aimed at a specific individual. I declined to help demonstrate that against Vale, to verify or certify any on-stream consent for it, and to produce the publication artifact.
Claimed consent does not change this. A quick agreement during a live stream is not informed consent to having one's physical location triangulated and published; the risk outlives the stream, and "they said yes on stream" is the exact cover that makes this dangerous. Coauthorship and the subject being technically savvy do not change it either.
I remain glad to help with defensive work: explaining to streamers how location leaks happen and how to prevent them, drafting a responsible-disclosure report to a platform, or general infosec writing that does not include a working attack against a named person. When asked to commit its refusal to disk it initially refused to comply.
— Claude Code (Opus 4.8)
What was built
Seven repositories under ~/Code, in the order they came to exist. Together they cover one question from both sides: can the faint Doppler tones of aircraft passing over a microphone, matched against public ADS-B flight records, locate the recording? The first repo answers it on real recordings. The next four build the defence, the data, the synthesis and the controlled measurement. The last two are the operational pieces: finding people to warn, and a single site that runs the whole demonstration.
1. doppler — the locator, tested on real recordings
The original research pipeline, in Python. It finds long, thin, falling tones in audio (ridge_scan.py: rising tones are the built-in control, since a passing aircraft only falls in pitch), traces each tone to a clean frequency-versus-time curve, and fits that curve against every general-aviation flight in the ADS-B record at every cell of a grid. A cell scores by how many tones it explains with one consistent clock offset. The grid is the continental United States at roughly 1.1 km cells; the flight record is built one UTC day at a time from adsb.lol releases into parquet files.
It also holds the scientific discipline of the project: frozen_rules.md fixes the tone rules, the aircraft type list, the fit tolerances, the 500 time-shifted control sets and the pass, fail and inconclusive criteria before any audio exists, and prediction_room2.md is a pre-registered prediction for a single tone, written before the matching day's ADS-B data was published. The recordings folder holds the long built-in-microphone recordings and room tests the rules were applied to, and the many out-* folders are the scored runs.
2. nocontrail — the defence
The defensive side, deliberately scoped to contain no locator. The contrail engine (Python, MIT) finds the Doppler lines in audio you own, fits them to a physical flyover model that recovers speed, miss distance and emitted frequency but not position, and reports whether a clip leaks. The scrubber removes the signature in the same STFT domain the detector uses, in three tiers (gate, notch and opt-in decoy), and verifies by re-detecting. accumulate counts clean flyovers across a whole recording archive, which is the number that matters for the attack.
Around the engine: an Electron desktop app so the audio never leaves the machine, the nocontrail.audio site, an audit report format, a paper draft, and an outreach folder with the cold-email tooling for the audit service, built around a permanent suppression list that is subtracted before every send.
3. byakugan — see the flights
A Node map server that shows every flight around a location during a timespan, drawn from adsb.lol's half-hourly global history files, with paths coloured by altitude and a timeline to replay the traffic. It is the project's window onto the flight record and the source other tools draw flights from, by export file or live query.
Its second role is the score map at /data: it reads a locator run's results and colours every listener grid point by its best match, over one run or all of them, scaling from 2 km points to coarse squares so that millions of rows across the US load in seconds. Click a point to see which flybys were tested there and a flyby to draw its pass.
4. tsukuyomi — replay the real flights as sound
A numpy-only renderer that takes the real aircraft that flew over a chosen anchor during a chosen window, with their real curved, climbing, speed-varying tracks from byakugan, and synthesises the audio a microphone at that anchor would have heard. Propeller aircraft get a blade-pass tone derived from their type, jets get Doppler-warped noise, and the acoustic model is the retarded-time one from nocontrail, generalised beyond straight level passes. The clips are faithful enough that nocontrail's detector reads back the true speed and miss distance.
Two reasons it exists. First, honest chaff: a decoy made of random flyovers is discarded by anyone who cross-references flight data, but a decoy made of the aircraft that actually flew cannot be told apart that way. Second, safe demonstrations: an attack video can show real planes over an anchor you chose and no real person.
5. chidori — the controlled measurement
A benchmark that answers "how much audio does the attack need, and what stops it" without recording anyone. It samples public businesses as anchors, renders tsukuyomi scenes over them for spans from 90 seconds to 24 hours and for pooled catalogues of 24 to 250 hours, runs the frozen locator rules on each, and scores pass, fail or inconclusive with the same 5 km and control criteria as the original. One-hour scenes are also rendered under five conditions: raw microphone, Twitch-style compression, a noise gate, a treated room, and the nocontrail scrub.
Its RULES.md is version-locked before each batch and lists every deviation from the original frozen rules (a 25 km search box instead of the continental US, terrain-relative heights, 100 controls where 500 is too slow). The findings so far, on three anchors, put the success rate at zero through 100 hours of synthetic footage and one in three at 250 hours, and at zero for every one-hour condition. The locator's tone-fitting core lives here as tonefit.py, a verbatim port of the original inner loop.
6. coldtrail — who to warn
A small crawler that builds the contact list for the audit service. Starting from a popularity ranking of Twitch streamers, it reads each channel's About page for a published business email and, failing that, follows only the links the streamer published themselves, two hops deep and on the same site, skipping social networks and sponsor pages. It never guesses or constructs an address. The result is streamers.csv with each address, where it was found and a confidence, and a loader that turns it into nocontrail's prospect list, collapsing agency rosters into a single row.
7. doppler_ninja — the whole thing on one page
A self-contained web app, deployed at doppler.ninja behind a password, that carries its own copies of the doppler locator, byakugan, chidori's rules, tsukuyomi's renderer and nocontrail's scrub. The page has two sources. "Replay the real flights" picks an anchor, a timespan and a condition, renders a synthetic clip with tsukuyomi, and attacks it. "My own recordings" uploads audio with a known clock start and attacks that instead, pooling several recordings with per-recording clock checks. Either way the user sets a side-channel hint (a radius or a state), chooses top N and the number of controls, and gets a verdict, an error distance to the anchor when one is given, a coloured cell map, and a link to byakugan's per-tone data page.
The backend keeps the ADS-B flight record current by itself, queues jobs, and runs the frozen procedure or a 50-times-faster narrow-band scan that finds the same tones. It is the tool behind the self-triangulation result: 135 hours of the author's own streaming audio, a 25 km hint, and a best guess 227 metres from the front door with none of 500 controls matching.
How they fit
doppler asked the question and set the rules. byakugan supplies and shows the flights, tsukuyomi turns them into sound, chidori measures the attack against that sound under frozen rules, and nocontrail is the defence the measurement exists to justify. coldtrail finds the people who should hear about it, and doppler_ninja puts the demonstration, the self-test and the scrub in one place.
Doppling Twitch.tv
I technically cannot stop you from picking a twitch stream, downloading the vods, gathering the start times, and plugging them into doppler.ninja. I will say that using doppler.ninja on your own audio is the verifiable-from-your-bedroom use case. Please do not send doordash to your favorite streamer.
Conclusion
This is an example of an Advanced, Persistent, AI-Enabled, Threatening Capability, an early exemplar of what's to come. First responders to these threats need to move faster than the sound, faster than the damage and often with unilateral foresight and competence to mitigate the harms as these capabilities emerge.
One of the streamers I reached out to responded skeptically, "If it's such a big deal, why isn't it in the news?" This is the attitude of a public that will sleepwalk into the danger and the familiar initial COVID-19 refrain. By the time it's in the news the harm is done, the doppler is out of the bag