This is my submission for the Hacktoberfest Open-Source AI Challenge: Week 1 — Touch Grass.
The problem with birding apps
The best birding happens exactly where your phone becomes a brick: deep forest, mountain trails, that marsh 40 minutes from the nearest cell tower. The moment a call you don't recognize echoes through the trees, the usual flow is: record it, hope you remember it later, upload it when you get home, wait for the cloud.
I wanted the whole loop to close on the trail. So I built trail-bird-id: point it at an audio file, get the species. No internet. No account. No API key. No per-call cost. Just a laptop (or a Raspberry Pi) and an open-weight model.
$ python identify.py trail_recording_07h42.mp3
Analyzing trail_recording_07h42.mp3 (fully offline, BirdNET V2.4, 6,522 species)...
1. Black-capped Sparrow (Arremon abeillei) conf=0.99 @ 0.0-3.0s
2. Streaked Saltator (Saltator striatipectus) conf=0.86 @ 3.0-6.0s
The build: embarrassingly little code
The heavy lifting is BirdNET — an open-source audio classifier from the Cornell Lab of Ornithology and Chemnitz University of Technology (CC BY-SA 4.0), trained on 6,522 species. It ships as a ~50 MB TFLite/TensorFlow model inside the pip package, which is the whole trick: installing the library means installing the brain.
My contribution is a ~60-line wrapper, identify.py, that runs the model in 3-second windows over any wav/mp3/ogg file and aggregates to the best-confidence detection per species:
uv venv --python 3.11 .venv
uv pip install --python .venv/bin/python birdnet-analyzer
.venv/bin/python identify.py your_recording.mp3 --top 5
That's the entire setup. No Docker, no GPU, no cloud credentials.
Does it actually work? Verified against ground truth
I don't trust demos that only show one cherry-picked clip, so I tested against four field recordings from Wikimedia Commons where the species is known from the recording metadata (xeno-canto / iNaturalist sourced):
| Recording | Expected species | Top-1 prediction | Confidence |
|---|---|---|---|
| Black-capped Sparrow XC250490 (Niels Krabbe, CC BY-SA) | Arremon abeillei | ✅ Black-capped Sparrow | 0.99 |
| Australian Magpie song (CC BY-SA) | Gymnorhina tibicen | ✅ Australian Magpie | 0.99 |
| Brown Hawk-Owl, South Bengal (CC BY) | Ninox scutulata | ✅ Brown Boobook (same bird, name updated by taxonomists — the model is more current than the file title) | 1.00 |
| Guira Cuckoo (CC0) | Guira guira | ✅ Guira Cuckoo | 0.95 |
4/4 top-1 correct, across four continents and four very different vocalizations (sparrow song, magpie caroling, an owl's hoot, cuckoo chatter). Runtime: ~6.7 seconds wall-clock for a 40-second recording, on a plain CPU.
Proving "offline" isn't marketing
"Works offline" is easy to claim. So I re-ran identification with all egress blocked — every proxy environment variable pointed at a dead localhost port, so any HTTP call from the Python stack would fail instantly:
$ env HTTPS_PROXY=http://127.0.0.1:9 HTTP_PROXY=http://127.0.0.1:9 \
ALL_PROXY=socks5://127.0.0.1:9 NO_PROXY= \
.venv/bin/python identify.py audio/australian_magpie.ogg
1. Australian Magpie (Gymnorhina tibicen) conf=0.99 @ 9.0-12.0s
Still works, because there is no network code to fail. The weights live in site-packages/birdnet_analyzer/checkpoints/. The trail is the deployment target, and the trail has no SLA.
Why open innovation matters here
This is where the open approach doesn't just match the closed one — it wins:
- It works where the closed apps can't. Zero-connectivity is the core use case, not an edge case. A cloud API is a non-starter on a ridgeline.
- Nothing leaves your device. Field recordings often capture more than birds — your voice, your location, your hiking companions. With a local model, that data never touches a server you don't control.
-
You can actually own it. Pin the model version, tune the confidence threshold for your region, swap in a species list for your local patch (
--lat/--lonfilters to species plausible for your coordinates), or fine-tune on your own recordings. Try doing that with a closed mobile app. - Cost at scale is zero. A citizen-science project processing 10,000 hours of soundscape pays nothing per call — because there are no calls.
Honest limitations
- BirdNET needs a reasonably clean recording; heavy wind or overlapping dawn-chorus can drop confidence. The 3-second windowing means very short calls can be missed.
- My test set is 4 recordings, not 4,000 — it's a smoke test with known ground truth, not a benchmark. (BirdNET's own peer-reviewed evaluations cover the rigorous part.)
- I built and validated this on real field recordings made by others; the full "take it outside" bonus round happens this weekend on my local trails — I'll update the repo with what it hears.
Links
- Repo: https://github.com/jeffreyturov-dev/trail-bird-id
- Model: BirdNET-Analyzer (CC BY-SA 4.0)
The screen is the shortest part of this experience: record outside, identify anywhere, get back to listening. 🌲
Top comments (0)