DEV Community

Jeffrey Turov
Jeffrey Turov

Posted on

I Put 6,522 Birds in My Backpack: Offline Bird Call ID on a Laptop, Zero Internet

Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass Submission 🌿

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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/--lon filters 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

The screen is the shortest part of this experience: record outside, identify anywhere, get back to listening. 🌲

Top comments (0)