Aurora
A voice-training studio for trans women, on the web and iOS.
- Expo
- React Native
- Swift
- TanStack Start
- Web Audio (AudioWorklet)
- Fastify
- Drizzle
- PostgreSQL
- Better Auth
- OpenAI
Overview
Real-time pitch, guided reading with live feedback, voice check-ins against a baseline, and spoken role-play with AI to take a trained voice into everyday situations. One tested TypeScript audio engine powers both the browser and the iPhone.
Context
For many trans women, voice training is a big part of transition, and most of it happens alone: a tuner app, recordings, and a lot of doubt about whether it's working.
Aurora is a practice studio in Brazilian Portuguese. It measures pitch live, guides reading sessions, compares check-ins against a personal baseline, and lets you rehearse real situations, like ordering a coffee or a phone call, in a spoken conversation with AI.
What I built
- A voice-capture engine in pure TypeScript: energy, pitch smoothing, octave-jump repair, session contour, voiced time and clip segmentation. The pitch detector is injected, so the package has no dependencies.
- The browser feeds it from an AudioWorklet and the iPhone from the native recorder. The web app is an installable PWA with an offline shell.
- iOS extras written in Swift: a Live Activity on the Dynamic Island that shows your pitch, and background audio through a small Expo module.
- Spoken role-play: hold to talk, transcription primed with the scene's vocabulary, and a streamed reply.
- Privacy by default: recordings stay on the device, audio only leaves it with explicit consent, and accounts can be deleted in the app.
Key decisions
01All the voice math in one pure package
Web and mobile only supply audio frames; everything else is shared. It runs in CI without a microphone and is covered by most of the test suite.
02From a course to a studio
The first version was a lesson track with XP. After writing design docs and researching the market, I pivoted to practice tools people can use every day, and cut features that didn't serve that.
03Turn-based voice instead of real-time speech-to-speech
Transcription followed by a streamed text reply is cheaper and far easier to control than a real-time voice model.
04Expo with targeted native modules
I built a SwiftUI spike, then kept Expo and wrote Swift only where the platform required it: the Live Activity and background audio.
05A rate limit that can't be spoofed
The auth layer rewrites the client IP header with the address the server actually resolved, so forging X-Forwarded-For doesn't reset the limit. There's a test for it.
What was hard
Real voices are messy. Breath, silence and the pitch detector jumping an octave all look like data. Most of the engine's work is deciding what not to trust, and the tests encode those cases.
Designing for a sensitive audience: streaks that can be paused without guilt, recordings that stay on the device by default, and explicit consent before any audio leaves it.