Recent bird sightings across the United States, on a real-geographic map. Pick a state or ZIP and see what's been spotted in the last two weeks — every sighting coloured and shaped by bird family. Live at bird-maps.com.
![]() |
![]() |
![]() |
| Statewide — California | City — San Francisco | Neighborhood — Golden Gate Park |
A single-page map over the eBird observation feed.
- The server clusters, not the browser — the API returns zoom-aware aggregated buckets at low zoom and clips to the chosen state with a PostGIS
ST_Intersectsquery, so a national point set never lands in the client all at once. - Bird family is the visual key — each observation is coloured and shaped by family (silhouettes from PhyloPic), so the map reads at a glance instead of as a wall of identical pins.
- Geography lives in the URL — the selected state is in the URL, so every view is a shareable link; a ZIP resolves to its state through a vendored Census index, no server round-trip.
- Cheap to run — the services scale to zero on Cloud Run; the only always-on cost is a small Postgres instance behind a billing cap.
npm install
npm run db:up # local Postgres + PostGIS via Docker
npm run db:migrate
npm run db:seed # ~400 sample sightings so the map isn't empty
npm run dev --workspace @bird-watch/frontendTo also run the API the map reads from, see services/read-api/README.md.
db:seed writes a deterministic, idempotent spread of ~400 observations across
~24 bird families and ~20 states — enough to render a realistic map without an
eBird API key. (Migrations only seed reference data: family silhouettes, state
boundaries, and a small species set; real sightings otherwise come from a live
eBird ingest.) Re-run it any time; it reads DATABASE_URL and defaults to the
local Docker database.
frontend/ React + Vite + MapLibre map → Cloudflare Pages
services/
read-api/ Hono HTTP API the map reads from → Cloud Run
ingestor/ scheduled eBird ingest + enrichment → Cloud Run Jobs
admin-api/ operator-only silhouette overrides → Cloud Run
packages/ shared db-client + TypeScript types
infra/ Terraform (GCP + Cloudflare) + edge Workers
The ingestor pulls from eBird on a schedule and upserts observations into Cloud SQL
Postgres (PostGIS), enriched with PhyloPic silhouettes and iNaturalist / Wikipedia
species detail. The frontend reads everything through the cached Read API behind
Cloudflare. Design rationale:
docs/specs/2026-04-16-bird-watch-design.md.
| Layer | Technology |
|---|---|
| Frontend | React 18 · Vite · MapLibre GL JS |
| API | Hono on Node |
| Data | Cloud SQL Postgres 16 + PostGIS |
| Infra | GCP Cloud Run · Cloudflare (Pages, R2, Workers) · Terraform |
| Sources | eBird · PhyloPic · iNaturalist · Wikipedia |
services/read-api/README.md— the public read APIservices/ingestor/README.md— ingest kinds and schedulesservices/admin-api/README.md— operator silhouette overridesinfra/README.md— Terraform and deploymentSECURITY.md— vulnerability disclosure
MIT — see LICENSE.


