← back to projects

BirdNet-Behavior

A ground-up Rust rewrite of BirdNET-Pi with behavioral analytics

lang Rust license Apache-2.0 view on GitHub →

Highlights

  • 400-600 MB down to 20-50 MB resident memory
  • 5-15 second cold start down to sub-one-second
  • Python, virtualenv, and pip replaced with a single static binary
  • Lock-free parallel audio processing with no GIL bottleneck
  • Feature parity with BirdNET-Pi (SQLite history, 18+ UI pages, Apprise, BirdWeather)
  • Optional behavioral analytics via duckdb-behavioral (dawn chorus, migration, co-occurrence)
  • Prometheus, MQTT, Home Assistant auto-discovery

400-600 MB down to 20-50 MB resident. 5-15 second cold start down to under a second. A shell-script install replaced with a single binary. Here’s how, and what the recovered performance budget was worth.

What the rewrite does

BirdNet-Behavior is a ground-up Rust reimplementation of BirdNET-Pi with feature parity to the Python original, delivered as a single static binary. No Python, no virtual environment, no pip dependency tree to resolve at install time. Cross-compile once, copy the binary onto the device, run it.

Feature parity means the user-visible surface is all there on day one: SQLite detection history, 18+ dashboard pages, Apprise notifications, BirdWeather integration, species info and media enrichment. A user migrating from the Python version should not feel like they’ve given something up.

Why the rewrite exists

BirdNET-Pi is a wonderful open-source project. It takes an academic acoustic-classification neural network, wraps it in a Python application, puts it on a Raspberry Pi, and listens continuously for birdsong. It has an active community and a global network of installed devices. It’s also one of the clearest illustrations I know of how Python’s runtime model breaks down at the edge of what a $35 single-board computer can do.

Installing it on a Raspberry Pi 3B+ is a minor odyssey of virtualenvs, pip, system libraries, a web stack, and a GIL-bound audio pipeline. Keeping it running unattended is another exercise in patience. Once you accept that “works for a year on a Pi” is the real shipping requirement, a rewrite stops being a yak-shave and becomes the honest path.

What the performance budget unlocked

The real payoff isn’t that the device runs cooler. It’s that a Pi Zero’s worth of headroom lets the same hardware run a behavioral analytics layer on top of the base detection pipeline. Using the duckdb-behavioral extension in-process, the application can sessionize raw detections into activity bouts, classify species as resident versus migrant by correlating detection density with the calendar, validate the dawn chorus window from the device’s own data, compute species co-occurrence matrices, and track long-running trends without a separate data pipeline. All of it runs in-process, against the same detection history the dashboard already reads. None of that would have fit on the Python runtime, because the performance budget simply wasn’t there.

Modernized output surface

Prometheus endpoint for scraping, MQTT with Home Assistant auto-discovery, and a photo-gallery lifelist UI with dynamic search and correlation heatmaps. The specific integrations matter less than the underlying point: once the runtime is small enough, adding them is a matter of hours rather than a “we’ll see if it still fits in RAM” negotiation.

What it demonstrates

Pick a real requirement (“runs unattended for a year on a $35 computer”), pick the runtime model that makes it trivially achievable (Rust, single binary, in-process database), and accept the upfront engineering cost because the operational payoff lasts as long as the device does. The deployment constraint is the starting point, the runtime choice falls out of the constraint, and everything after that gets easier.

Measurement methodology

The memory numbers are measured with smem -k against a running process (resident set size, not virtual), on a Raspberry Pi 4B (4 GB) running Debian 12 bookworm with the default inference model loaded and an active audio capture thread. The comparison is apples-to-apples (same Pi, same model, same audio device, same elapsed runtime) against a stock BirdNET-Pi install from the upstream install script. The methodology script lives in the repository under scripts/measure_memory.sh, and the cold-start timing is systemd-analyze’s “service active” delta.