This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass
What I Built
Chirp Trail is an offline bird-call companion that runs on a laptop or Raspberry Pi in your backpack. You press one button to record a few seconds of birdsong, put the device away, and look up at the trees. It identifies the species, and a small local language model explains it in plain language: what the bird is, what it eats, and how to spot it. Every sighting is saved to a trail log.
The screen is the shortest part of the experience. Species cards and the log are for later, back at the trailhead or at home.
It's for beginner birders, hiking groups, school nature clubs, and anyone who walks where there's no cell signal.
Demo
Chirp Trail runs entirely on-device, so there is no hosted link. Here is what one interaction looks like.
Flow on the trail:
- Tap the big Listen button and hold the device toward the trees for about 6 seconds.
- Put it back in your pocket and look up.
- Check the species card when you stop for a break.
Example output (illustrative):
Heard: Northern Cardinal (confidence 0.87)
Also possible: Carolina Wren (0.41), Tufted Titmouse (0.22)
A bright red songbird with a crest. Males sing loud, clear whistles
from high perches. Look for it near dense shrubs and forest edges.
It eats seeds, fruit and insects.
Saved to your trail log: Oct 9, 07:42
When confidence is low, the card says "Not sure" and lists the top three guesses instead of picking one.
Code
The core pipeline is small. This is the heart of it: classify the audio locally with BirdNET, then ask a local model to explain the result.
import csv, glob, subprocess, sqlite3, datetime
import ollama
MIN_CONF = 0.5
def identify(wav_path, lat, lon, week):
"""Run BirdNET locally, filtered by location and time of year."""
subprocess.run([
"python", "-m", "birdnet_analyzer.analyze", wav_path,
"--lat", str(lat), "--lon", str(lon), "--week", str(week),
"--min_conf", "0.1", "--rtype", "csv", "-o", "out",
], check=True)
rows = []
for f in glob.glob("out/*.csv"):
with open(f) as fh:
rows += list(csv.DictReader(fh))
rows.sort(key=lambda r: float(r["Confidence"]), reverse=True)
return rows[:3]
def explain(species, db):
"""Ground the LLM in local facts so it doesn't guess."""
facts = db.execute(
"SELECT habitat, diet, field_marks FROM species WHERE common_name=?",
(species,),
).fetchone()
prompt = (
f"Write a 3-sentence field-guide note about the {species}. "
f"Use only these facts: {facts}. Keep it friendly and simple."
)
reply = ollama.chat(
model="qwen2.5:3b",
messages=[{"role": "user", "content": prompt}],
)
return reply["message"]["content"]
def run(wav_path, lat, lon, week):
db = sqlite3.connect("chirp.db")
top = identify(wav_path, lat, lon, week)
if not top or float(top[0]["Confidence"]) < MIN_CONF:
return {"status": "not_sure",
"guesses": [r["Common name"] for r in top]}
species = top[0]["Common name"]
note = explain(species, db)
db.execute("INSERT INTO log(species, ts) VALUES (?, ?)",
(species, datetime.datetime.now().isoformat()))
db.commit()
return {"status": "ok", "species": species, "note": note}
How I Built It
- Audio classification: BirdNET-Analyzer runs locally on short 3-second audio chunks. It's open source and needs no network connection.
- Explanations: A small open-weight LLM (Gemma, Qwen, or Llama in a 3-8B quantized build) runs through Ollama. It turns the classifier output (species, confidence, location, time of year) into a short field-guide-style note.
- Local data: Species facts and regional checklists live in SQLite, so the LLM is grounded in a local dataset instead of guessing. The trail log is stored in the same database.
- Interface: A minimal one-button web UI served from the device itself, with a big record button, a species card, and a log view.
-
Pipeline:
- Record audio.
- BirdNET returns candidate species.
- A filter drops species that aren't plausible for the location and month.
- The LLM writes the explanation.
- The sighting is saved offline.
- Honesty guardrail: Below a confidence threshold, the app says "not sure" and shows the top three guesses instead of inventing a confident answer.
Why Does Open Innovation Matter?
- It works where closed APIs can't. Birds sing where there's no signal, so the whole pipeline has to run on-device. A cloud API fails exactly where this app is needed.
- Your location data stays yours. A log of where you were and when, tied to rare-species sightings, can be sensitive. Local inference keeps it on your device.
- It costs nothing to run. There are no API keys, per-call fees, or subscriptions, which matters for school clubs and community groups.
- It's swappable and tunable. I can swap in a regional species checklist, tune the explanation style for kids or experts, or replace the LLM without rewriting the app. A closed model can change or disappear overnight.
- It's inspectable. Birders can check how the classifier behaves, add local species, and contribute fixes, so the tool improves through community effort.
Top comments (0)