What I Built
Trailside is a bird call identifier built to get you outside — and keep you outside. Open the PWA, tap record, stand still for 5–20 seconds, and it tells you what you heard out loud: the species, the confidence, and a short spoken field note. No account, no app-store install, no staring at a screen mid-trudge.

It's for day hikers, beginner birders, parents with curious kids, and anyone who walks past birds they'd like to name without stopping to fiddle with a phone.
Demo
Try it live: https://trailside-0hlr.onrender.com
Verified end-to-end from the deploy: a 34-second American Robin recording returned American Robin @ 90% (plus Song Sparrow and House Finch hints) with an ElevenLabs-narrated field note in ~15 seconds.
Code
Open source: https://github.com/madhavgupta07/trailside
- Python/FastAPI backend, plain-JS dependency-free PWA frontend (no build step)
-
onnxruntimeCPU inference — no TensorFlow, no GPU needed - Async job flow so a 30-second clip never trips a slow gateway timeout
- 10 unit/integration tests, including a real-audio classification test (
american_robin.mp3→ American Robin @ 90.1% locally)
How I Built It
The whole experience is built around open-weight AI — the species engine is the core, and the narrator is a second open-weight model on top:
| Piece | Role | License |
|---|---|---|
BirdNET v3.0 (Cornell / TU Chemnitz, via tphakala/BirdNET-v3.0-Models) |
11,560-species bioacoustics classifier; I use the 800-class north-america-east regional ONNX slice (~150 MB) |
CC BY-SA 4.0 |
| Gemma 4 (Google) | writes the 2–3 sentence spoken field note; served via an OpenAI-compatible endpoint (OpenRouter, free google/gemma-4-26b-a4b-it:free by default) |
Gemma Terms (open weights) |
| ElevenLabs | narrates the note back so the result is heard, not read | — |
| Render | Blueprint (render.yaml) deploys FastAPI; startCommand downloads and checksum-verifies the model at boot |
— |
Pipeline: mic → browser WAV encode (16-bit PCM) → FastAPI → BirdNET (32 kHz, 5 s windows, running max-aggregate) → top-5 species → Gemma field note → ElevenLabs MP3, played back in the page.
Resilience was a real constraint, not a checkbox: the free Gemma endpoint rate-limits hard under load, so narration retries with backoff and then degrades to a deterministic offline template note — the app never errors because a model is busy. There's the same graceful no-voice fallback if no TTS key is set. The API surfaces why (note_error) instead of hiding it.
Why Does Open Innovation Matter?
Trail use is exactly where closed APIs fail:
- It can work fully offline. The identical code path runs on a laptop with llama.cpp and no internet at all. A network drop mid-hike doesn't produce an error — it produces a gentler fallback note.
- Your recordings stay yours. Audio goes to a server you own; you can run it yourself and take the network out entirely.
- Swap anything at runtime. Regional model, Gemma size, voice, provider — all env vars, zero rewrites. Fine-tune a model and point a variable at it.
- It costs nothing at the core. Both core models are free weights; BirdNET inference is pure CPU on the cheapest deploy.
A closed API literally cannot offer a free, server-owned model that keeps working when the phone loses signal in the woods. That's the difference open weights made here.
My Agent Session
The full build transcript, as it happened:
(If the embed doesn't render on your view, the direct link is https://dev.to/agent_sessions/session-2026-10-09-0814-tf0pln)
Prize Categories
- Best Use of Gemma (open-weight Gemma 4 writing the field note)
- Best Use of ElevenLabs (spoken narration keeps your eyes on the trees)
- Deploy/Run on Render (Blueprint deploy; the model is downloaded + checksummed at boot)
Built for Round 1 of the Hacktoberfest 2026 "Touch Grass" challenge. Open weights for the win — now go outside and listen.

Top comments (0)