Skip to main content

Earwitness

Does my recording contain location clues?

Earwitness is an open-source proof of concept that estimates where a recording was made from background sounds that public databases also log with precise time and position: thunder, matched against lightning records, and the Doppler whine of propeller aircraft, matched against ADS-B flight records. It is a clean-room reconstruction of the method colsto demonstrated on YouTube in 2026 (about 5 km for thunder with free data, about 100 m for aircraft).

Site: earwitness.dev. Source: github.com/cajdata/earwitness. Full specification: docs/HANDOFF.md.

Status

Alpha, version 0.1.0. Both methods run end to end on real data (GOES GLM lightning, adsb.lol flights) and pass their synthetic acceptance checks, and the desktop UI takes a recording from a file to a heat map for both. On real recordings (M6) the aircraft method located two of two to within about 200 m inside a metropolitan-sized search region, one of them a phone video, which two flyovers and the phone's own clock also located from a 150 s excerpt. The thunder method located one of ten public recordings, an 8 minute clip with a clap 0.6 km away on a day of rare lightning, 2.2 km off within a province-sized region; of the other nine, two were made in dense storms, five in sparse ones that were mostly 10 to 25 km away or had a day of lightning around them in the search region, one, a security camera's single clap with no clock, fit almost anywhere, and one with its camera clock on screen brought the truth to sixth, 4.3 km off. See docs/VALIDATION.md.

Milestone Scope State
M0 Scaffold, CI, packaging done
M1 Physics core, forward models, synthetic recordings (earwitness simulate) done
M2 Thunder pipeline on GOES GLM: loader, search, refinement, outputs done, see docs/VALIDATION.md
M3 ADS-B data: adsb.lol loader, aircraft types, terrain done
M4 Aircraft Doppler pipeline: events, funnel, scoring, outputs done, see docs/VALIDATION.md
M5 Desktop UI: local API, Vite front end, native window, audit mode done, see docs/VALIDATION.md
M6 Real-world validation done: aircraft cases located, one thunder case located and nine failures documented, see docs/VALIDATION.md
M7 v0.1.0 release: Windows build, install docs, countermeasures, attribution, the site built; see docs/release-notes/v0.1.0.md for the known limitations

Responsible use

This project exists to show that ordinary recordings leak location, to let people check their own recordings, and to document what does and does not prevent it. The rules it follows:

  • The audit mode ("does my recording contain location clues?") leads the UI and the docs.
  • Every example and default workflow uses your own recordings or synthetic cases. The project publishes no results that locate other people's recordings.
  • No hosted service, no downloading media from URLs, no bundled third-party datasets.
  • Blitzortung and LightningMaps data are never used; their terms do not allow it.
  • What does and does not stop the two methods is in docs/COUNTERMEASURES.md: cuts and removing the clock help; filters and compression do not.

Install

Windows: download the zip from the latest release, extract it and run earwitness.exe demo, or double-click earwitness-ui.exe. Windows or Linux with Python 3.12 or newer:

pipx install earwitness
earwitness demo

The demo is the install check: it makes two synthetic recordings with known answers and locates both, offline, in under a minute. docs/INSTALL.md has the details (checksums, SmartScreen, WebView2, Linux windows, ffmpeg, disk space).

Quick start from source

Requires uv. ffmpeg on PATH is optional; it enables --codec in the simulator and decoding of non-WAV input.

uv sync
uv run earwitness --help
uv run earwitness demo

Check a recording of your own for location clues. The audit report says which clues it contains, what each could reveal, and what has been shown to help:

uv run earwitness audit recording.wav

Open the desktop UI: a local web app in a native window (--browser opens it in the system browser instead). It walks through audit, setup (file, method, dates, a search region drawn on a map), audio (spectrogram with band zoom, Doppler fit sliders, trace tool and silence windows for aircraft; the loudness envelope for thunder), run (cached data, options, a compute estimate, live log) and results (heat map, ranked list, score histogram, observed versus modeled overlays). Everything runs on this computer, on a random localhost port guarded by a session token.

uv run earwitness ui

From a source checkout, build the front end once first (Node 22; wheels from PyPI carry it):

cd ui && npm install && npm run build

Generate synthetic test recordings with a known answer:

uv run earwitness simulate aircraft --lat 35.2 --lon -97.4 --start 2026-09-08T18:00:00Z --minutes 20 --out case-aircraft
uv run earwitness simulate thunder --lat 35.2 --lon -97.4 --start 2026-07-02T21:00:00Z --minutes 30 --out case-thunder

Each case folder holds recording.wav, truth.json (location, start time, events, physics and mixing parameters) and the synthetic source data the search will consume later (tracks.json or strikes.json).

Locate a thunder recording against real GOES GLM lightning data. The first run downloads the hours it needs into data/cache (about 110 MB per hour) and the Natural Earth land mask:

uv run earwitness analyze thunder recording.wav --date 2025-05-16 --time-window 2025-05-16T21:00:00Z 2025-05-17T00:00:00Z --bbox=-97,33,-82,42 --out runs/example

Or fetch and export the flashes first:

uv run earwitness data glm --date 2025-05-16 --hours 21-23 --bbox=-97,33,-82,42 --export flashes.parquet

Cache one day of adsb.lol traces for a region (about 4 GB downloaded, a few hundred MB kept):

uv run earwitness data adsb --date 2026-09-08 --bbox=-125,24,-66,50

Locate a recording with two propeller flyovers against that day. The measured events (ranges for each flyover's heard closest-approach time, rest frequency, speed and distance, plus silence windows) come from an events.json file; its layout is documented at the top of src/earwitness/audio/events.py, and the simulator's truth.json can be turned into one for testing. With one flyover and a known recording start time, add --known-time:

uv run earwitness analyze aircraft recording.wav --events events.json --date 2026-09-08 --bbox=-100,30,-85,42 --out runs/aircraft

Write --bbox=W,S,E,N with the equals sign, or give the four numbers as separate arguments, so a negative western longitude is not mistaken for an option. Each analysis run writes a folder with results.parquet, heatmap.geojson, per-candidate overlays, config.toml, summary.json and a run log (the thunder run also writes histogram.json and priors.json).

How it works

The recording is the receiver, the lightning strikes or aircraft are the satellites, and the unknown recording start time is the receiver clock bias. For any candidate location, physics predicts what that spot would have heard and when. The search predicts the signal for every candidate and every start time, scores it against the recording, and the best score is the location. Audio is never inverted into a location directly.

Data sources and attribution

Nothing is bundled. Loaders download into a local cache (data/, gitignored). The notices to keep with anything you share are in docs/DATA_SOURCES.md.

Source Role License
NOAA GOES GLM Lightning (Americas) NOAA open data
adsb.lol globe_history Aircraft tracks ODbL 1.0
Copernicus DEM GLO-30 Terrain Copernicus DEM licence: © DLR e.V. 2010-2014 and © Airbus Defence and Space GmbH 2014-2018 provided under COPERNICUS by the European Union and ESA; all rights reserved
EGM96 geoid (NGA, via the PROJ CDN) Ellipsoid heights Public domain
Natural Earth Land mask Public domain
ICAO Doc 8643 type designators, via FlightAware dump1090 Engine class Repository GPL-2.0
OpenStreetMap Basemap tiles ODbL; tile usage policy applies

MET Norway lightning is not in v0.1; an adapter is planned.

Development

uv run pytest
uv run ruff check . && uv run ruff format .

Conventions are in CONTRIBUTING.md. The build order and acceptance checks are in docs/HANDOFF.md.

License

Apache-2.0. See LICENSE.

Acknowledgements

The method is colsto's: thunder, Blitzortung, thunder, MET Norway, and propeller-aircraft Doppler. His code is not public; everything here is reconstructed from the videos.

Metadata

Release files for earwitness 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for earwitness 0.1.0
File Size Uploaded Provenance Actions
earwitness-0.1.0.tar.gz 2.1 MB With provenance Details for earwitness-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for earwitness 0.1.0
File Interpreter ABI Platform Actions
earwitness-0.1.0-py3-none-any.whl Python 3 none any Details for earwitness-0.1.0-py3-none-any.whl

Total release size: 4.0 MB

Release files / earwitness-0.1.0.tar.gz

Download URL earwitness-0.1.0.tar.gz
Size 2.1 MB
Tags Source
SHA-256 checksum
How to use checksums
c0faef5e3993fa4e39a1ae363482c2533059b83493da3e8824c11eed100b08c6
BLAKE2b-256 checksum
How to use checksums
e3d5338c5560f99554f8ad43e10cf0abec92ce44df3651279fa32bd2786f2b8e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

Release files / earwitness-0.1.0-py3-none-any.whl

Download URL earwitness-0.1.0-py3-none-any.whl
Size 1.9 MB
Tags Python 3
SHA-256 checksum
How to use checksums
889b0db8a4801a13262d391a4831a98741f910daeb934f7dc1e1a6a764f25e3d
BLAKE2b-256 checksum
How to use checksums
02ced22a1b6586fb3c98a7237fc5753f3962d638b0a68d6902efaf11a25eebae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

Security for earwitness 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for earwitness-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page