2025 · civic tech · location-aware · AI assistant
Discover Sacramento
Guided voice tours for locals and tourists in one mid-sized city
why
A guided walking tour for Sacramento, built for both locals and tourists. Most “explore the city” apps are search engines with a map skin: they hand you a pin, a card, a phone number, and disappear. I wanted something closer to a walking companion. A voice that talks while you walk, names what used to be on this corner, fills in what the building is hiding.
Sacramento is the test case because mid-sized cities get worse coverage than they deserve, because the history is denser than the surface lets on, and because if the shape works here it’ll work anywhere with a dense enough catalogue.
what I built
A location-aware catalogue of 115 places across the city, each one a structured record (era, kind, sources, coordinates) plus a short script generated from those fields by Lovable’s AI Gateway. The script goes to ElevenLabs for text-to-speech. The audio plays in the headphones; the map pins move with the listener.
Architecturally it’s a thin React app over Supabase. Edge functions broker every external call: Mapbox tokens, ElevenLabs keys, Google Places lookups never reach the client. The catalogue is the asset; everything else is plumbing.
my voice, badly
The whole product is guided voice tours: a local or a tourist walks the city while someone tells them what was here before. The someone had to be a person, not a stock TTS voice. So I cloned mine in ElevenLabs and piped the place scripts through it.
It doesn’t sound anything like me. The cadence is close, the vowels are close, but the warmth isn’t there. A professional-tier clone would probably close the gap; I haven’t paid to find out. The starter plan stays. The tour still works.
Audio generates at page load right now, which is the wrong answer. Each place should be cached after the first play, or pre-warmed for the canonical 115. TTS is the expensive call in the stack and there’s no reason to pay for it twice. I know what the fix is. I haven’t shipped it.
decisions I’d defend
Edge functions as secret broker. No third-party key reaches the client. Every Mapbox tile request, every ElevenLabs call, every Google Places lookup runs through a Supabase edge function that holds the credential. The client sees a signed URL or a buffered audio response and nothing else. Overbuilt for a side project; the right shape for the production version.
Structured place data before generated copy. The script is not the source of truth. Every place is a row (era, kind, address, sources, short editorial summary) and the model writes the script from those fields, never around them. If a fact is wrong, you fix the row and regenerate. The model doesn’t talk to the user directly. The voice does, and the voice reads from a script the data shaped.
Audio as enrichment, not the spine. When ElevenLabs fails (quota, network, moderation) the place card has to work silently. The tour degrades to a quiet map. That fallback behavior was the first thing I designed, not the last.
what I learned
The hardest part wasn’t the AI. The model writes a serviceable place script from structured fields the first time you ask it; tightening voice and tone is a few iterations on the prompt. The hardest part was the catalogue: deciding which 115 places get a pin, sourcing dates and history, naming the era a building belongs to when it’s lived through three. AI eats the easy work. The editorial work compounds.
The voice clone surprised me. I expected the limitation to be obvious-bad and ship-blocking; it turned out to be subtle-bad and ship-permissive. The failure mode is uncanny, not broken.
what’s next
Caching, first. Then a contributor flow for adding places without touching code. Maybe a second city, picked by whoever cares enough to seed it.
See also
All projects- Celine AI Same architecture in a different jurisdiction: edge functions broker every external call, the client never sees a key. Celine guards lien statutes; Sacramento guards the audio bill.
- GardenIQ Rules-first, AI-second, a third time. The model writes the script; structured place data shapes what the script can say.
Working on something similar?
I take a small handful of consulting briefs a year and am always up for trading notes with anyone shipping in this space — send a note.
Or: values behind the work · obsessions that shape it · other projects.