Detect and clip bird vocalizations in continuous field recordings, stored on Backblaze B2. Upload a passive-acoustic recording (WAV/FLAC), run the official on-device BirdNET-Analyzer engine, and browse a per-recording detection timeline of which species were heard and when — with every artifact (detections JSON/CSV, event clips) written back to B2.
This is a full-stack sample built on Backblaze's vibe-coding starter kit: a TypeScript + Python monorepo with a dashboard UI, a full-bucket file explorer, and a FastAPI backend with strict layered architecture and structural tests.
What it does:
- Ingest continuous field recordings + survey metadata (site, lat/lon, date) to
recordings/<id>/on B2 — sustained raw-audio landing in object storage. - Detect bird species/sound events on-device with kahst/birdnet-team's official
birdnet-analyzer(BirdNET GLOBAL 6K V2.4 model — auto-downloads once (~224 MB) on the first detection, then cached locally; no API key; CPU by default). No second API key: B2 credentials are the only secret. - Clip an event-aligned WAV per detection back to B2 (
recordings/<id>/clips/) — the write-amplification a PAM pipeline produces. - Timeline every detection over the recording duration, grouped/colored by species, with a species summary and inline clip playback via B2 presigned URLs.
Dashboard — passive-acoustic metrics (recordings, detections, species, audio-hours, event clips, storage used), a detections-by-recording chart, and a recent-recordings table.
Recordings — the survey library of every field recording under the recordings/ prefix on B2, with its survey date, duration, detection and species counts, and a re-run control.
Detection timeline — a per-recording view: a species-colored detection timeline across the recording, a species summary with scientific names and max confidence, and event clips streamed from B2 presigned URLs.
Passive-acoustic monitoring is a storage-heavy workload: recorders produce hours of raw
audio continuously, and analysis multiplies each raw file into detection tables and many
small event clips. B2 is the single durable layer for all of it — raw source/ audio,
meta.json, detections.json + detections.csv, and every clips/*.wav — accessed
through the S3-compatible API with presigned URLs for in-browser playback.
Detection uses the official birdnet-analyzer package from kahst/birdnet-team (the
BirdNET-Analyzer project) — not the third-party birdnetlib wrapper. It runs the
BirdNET GLOBAL 6K V2.4 model, which birdnet-analyzer auto-downloads once (~224 MB)
on the first detection and then caches locally — no API key, CPU by default, so the
first detection is slower while the model downloads and warms up. The model
weights are licensed CC BY-NC-SA 4.0 (non-commercial use + attribution + share-alike).
Review that license before any non-research or commercial use. See
docs/features/detection.md.
You need: Node.js >= 20, pnpm >= 9, Python >= 3.11, and a free Backblaze B2 account.
Local scripts are supported on macOS, Linux, and WSL2. Native Windows is not supported
yet because the dev scripts use POSIX shell syntax and services/api/.venv/bin/* paths;
use WSL2 on Windows. Detection runs on CPU by default; it uses an NVIDIA GPU only
when CUDA is available. BirdNET's TensorFlow/TFLite backend has no usable Apple MPS
backend, so on macOS (Apple Silicon) detection runs on CPU.
1. Run setup
pnpm run setupThis copies .env.example to .env (only if missing), installs workspace dependencies,
creates services/api/.venv, and installs the API's committed Python 3.11 resolution
from services/api/requirements.lock — including the heavy BirdNET/TensorFlow tree, so
the first run takes a while and the first detection warms the model up for a few seconds.
Use the
pnpm runform:setup(likedoctor) is a built-in pnpm command before pnpm 11, so barepnpm setupwould run pnpm's own command instead of this script.
2. Add your B2 credentials
Open .env and fill in the standard B2_* values. In the
Backblaze B2 dashboard:
- Create a bucket and note its region — B2 shows it in the bucket's S3 endpoint,
e.g. an endpoint of
s3.us-west-004.backblazeb2.commeansB2_REGION=us-west-004. The app derives the S3 endpoint from the region automatically.- Bucket Unique Name →
B2_BUCKET_NAME - region (from the endpoint) →
B2_REGION
- Bucket Unique Name →
- Create an application key with
Read and Writeon that bucket:- keyID →
B2_APPLICATION_KEY_ID - applicationKey →
B2_APPLICATION_KEY(only shown once — paste it now)
- keyID →
B2_PUBLIC_URL_BASE is optional (only for a public bucket); the app falls back to
presigned URLs without it. See docs for
creating a bucket
and app keys.
3. Run it
pnpm devFrontend at localhost:3000, API at localhost:8000. Open Recordings → New
recording, upload a short WAV/FLAC, then Run detection and watch the timeline
populate. Interactive API docs (Swagger UI) are at localhost:8000/docs.
pnpm dev runs the preflight pnpm run doctor first — it catches the common gotchas
(wrong Node/Python version, missing venv, missing or placeholder .env, ports taken).
A fresh clone has an empty bucket. To have something to run without hunting for audio, seed one short public-domain / CC0 bird recording (~30–120 s):
- Fetch a short CC0 clip. Two documented options:
- BirdNET-Analyzer ships an example soundscape — after
pnpm run setup, find it underservices/api/.venv/.../birdnet_analyzer/example/and copy a*.wavout. - Download a CC0-licensed recording from xeno-canto (filter the license to CC0) and save it locally as a WAV/FLAC.
- BirdNET-Analyzer ships an example soundscape — after
- In the app: Recordings → New recording, choose the file, set a site name, and ingest. Then click Run detection.
Large binary audio is intentionally not committed to this repo — seeding fetches or copies a clip locally at run time.
- Keep the UI kit (
apps/web/src/components/ui/+ tokens inglobals.css+/design). - Keep the full-bucket File Explorer (
/files) and generic Upload (/upload) — the reusable B2-backed scaffolding. The domain ingest is the Recordings create flow. - Rebrand by editing
apps/web/src/lib/app-config.ts(APP_NAME,APP_DESCRIPTION).
Full contract: AGENTS.md §2 — Building on This Starter Kit.
- Recordings — ingest, list (sample-scoped explorer),
edit, delete, and run detection over the
recordings/prefix - BirdNET Detection — on-device engine, model + license, parameters, CPU/GPU, JSON/CSV outputs, event clipping
- Detection Timeline — per-recording timeline, species summary, and clip playback from B2 presigned URLs
- Dashboard — PAM metrics (recordings, detections, species, audio-hours, clips, storage used)
- File Browser — browse the entire bucket
- File Upload — generic drag-and-drop B2 upload
- Metadata Extraction — checksums, image/PDF info
- Design System — tokens, primitives, error/empty states. Live at
/design.
- TypeScript, Next.js 16, React 19, Tailwind v4, shadcn/ui, Recharts, TanStack Query
- Python 3.11+, FastAPI, boto3, Pydantic v2,
birdnet-analyzer(TensorFlow),soundfile - Backblaze B2 (S3-compatible object storage)
- pnpm workspaces (monorepo)
| Command | What it does |
|---|---|
pnpm run setup |
Idempotently copy .env.example to .env if missing, install deps, create the backend venv, install the locked API dependencies |
pnpm run doctor |
Preflight environment check (also runs before pnpm dev) |
pnpm dev |
Start frontend + backend |
pnpm dev:web |
Frontend only |
pnpm dev:api |
Backend only |
pnpm contract:export |
Export deterministic FastAPI OpenAPI JSON to docs/api/openapi.json |
pnpm contract:check |
Verify the checked-in OpenAPI artifact and frontend route registry |
pnpm check:agent-docs |
Validate agent shims, command docs, CI claims, and .env ignore coverage |
pnpm verify |
Credential-free canonical non-live suite — check:agent-docs, verify:api, verify:web |
pnpm verify:api |
Backend half: API lint, API tests, structure tests |
pnpm verify:web |
Frontend half: web lint, web unit tests, web typecheck + build |
pnpm verify:full |
pnpm run doctor, then pnpm verify, then Playwright E2E |
pnpm build |
Build frontend |
pnpm lint |
Lint frontend |
pnpm lint:api |
Lint backend (ruff) |
pnpm test:web |
Frontend unit tests (vitest) |
pnpm test:api |
Backend tests (pytest) |
pnpm check:structure |
Verify layering rules |
pnpm test:e2e |
Playwright E2E smoke tests |
pnpm verify needs neither B2 credentials nor a browser: the detection and audio tests
mock birdnet_analyzer + B2, so the suite stays offline. Real BirdNET runs when you
pnpm dev with a populated .env. See
docs/dev-workflows.md for the API
dependency refresh workflow.
Detection needs a real Python runtime with TensorFlow and does long-running,
larger-than-serverless work, so deploy the API to a container host such as
Railway (see infra/railway/) rather than a serverless Function
platform. The Next.js web app can deploy anywhere; point NEXT_PUBLIC_API_URL at the
API origin and set the API's API_CORS_ORIGINS to the exact web origin. The API is
unauthenticated and bucket-wide by design — use a dedicated B2 bucket/prefix and key for
any shared deployment. Deploying is a human-approved action.
| Doc | Purpose |
|---|---|
| AGENTS.md | Agent table of contents — start here |
| ARCHITECTURE.md | System layout, layering, data flows, recordings/ layout |
| docs/features/ | Feature docs (recordings, detection, timeline, dashboard, files) |
| docs/app-workflows.md | User journeys (ingest → detect → clip → timeline) |
| docs/dev-workflows.md | Engineering workflows and testing |
| docs/SECURITY.md | Security principles |
| docs/RELIABILITY.md | Reliability expectations |
| docs/exec-plans/ | Execution plans and tech debt tracker |
MIT License for the sample code — see LICENSE. The BirdNET GLOBAL 6K V2.4
model weights that birdnet-analyzer downloads and uses are CC BY-NC-SA 4.0 and
are not covered by this repository's MIT license.
Manage Backblaze B2 from your terminal using natural language (list/search, audits, stale or large file detection, security checks, safe cleanup).
Repo: https://github.com/backblaze-b2-samples/claude-skill-b2-cloud-storage


