Aug 2026 · 4 min read
Building Songora: Rendering a Full Song on a Phone Without Sending Audio to the Cloud
I shipped Songora — describe an idea, get a song. The interesting part wasn’t the prompt. It was rendering melody, vocals, and a wavetable mix on-device, and refusing to fake a vocal track when the phone couldn’t sing.
_b_Editorial_portfolio_.png&w=3840&q=75)
I shipped Songora — a beginner-friendly AI music studio. Describe an idea (or drop your own lyrics), pick a genre and voice, and listen.
The product looks like a prompt. The engineering is a render pipeline.
Two generators, one honest product
By default, Songora can call a cloud path for original lyrics and a spoken vocal preview. That’s useful. It is also not a full band.
When the network is gone — or you force local mode — the app falls back to an on-device sandbox renderer: device TTS + a wavetable instrumental, mixed on the phone.
Quality differs on purpose. I didn’t want offline mode to pretend it was the same studio as the cloud preview.
The interesting problem became:
Can a phone produce a complete song — melody, vocals, drums, bass, mix — without locking the UI, and without lying when a stage fails?
The local pipeline
SongAudioEngine runs in visible stages:
- Melody — an
ArrangementBuilderturns lyrics + genre/mood/tempo/energy into sections, phrase slots, and notes. Creativity shifts the seed so a re-roll actually re-rolls the tune. - Vocals — each lyric line is synthesized on the main isolate (platform TTS channels). Melodic processing happens later, off-thread.
- Instrumental — wavetable layers (pads, chords, bass, arps, drums, per-genre sound design) inside a Dart isolate.
- Mixing / finalizing — one master buffer. If RMS is essentially silence, throw. Don’t export a blank file and call it done.
A 90-second song is meant to render in a few seconds on a mid-range phone. That’s why synthesis stays wavetable, not a sample library, and why the heavy DSP never sits on the UI isolate.
Vocals: stretch, don’t autotune-by-default
Raw TTS is spoken, not sung. VocalEngine resamples, trims silence, then either:
- fits the line into its rhythmic slot (WSOLA time-stretch) so delivery stays human, or
- splits across melody notes and pitch-shifts when melodic mode is on
Choir is doubling and tiny pitch offsets. Voice styles are gentle formant/pitch colour — not a fake gender swap on a device that only ships one system voice.
The rule that mattered more than the DSP:
If more than half the phrases fail to synthesize, throw VocalSynthesisException. Do not ship an instrumental and label it as having vocals.
That matches a pattern I keep shipping: VidWeave doesn’t “almost” honour a crop. iRun doesn’t invent a finish time. Songora doesn’t invent a singer.
Jobs that survive a kill
Generation is a job with a persisted stage. Kill the app mid-render and it resumes on next launch. Credit spend/refund is keyed by job id — a song can’t be double-charged; a failed generation auto-refunds.
Moderation runs before generation (harmful prompts, artist impersonation). The local ledger is the sandbox source of truth.
What I shipped
Flutter, Riverpod, Hive, just_audio for playback (background + lock screen), a Lyrics Studio (rewrite, shorten, expand, rhyme, translate), occasion templates, and a library with retry/download.
No AI keys in the binary. Cloud credentials stay on a proxy. Local auth and purchases are labeled sandbox until the store backends are wired.
Store links:
What I’d still harden
Cloud audio is still a vocal preview, not a produced track with a real band. Owning the proxy (keys, quotas, a fuller music backend) is the next product leap. iOS TTS quality needs a dedicated device pass. And the local renderer will always sound like a sandbox next to a studio model — the job is to keep that gap visible, not papered over.
The bet: offline should still make a song, and failure should still sound like failure.
If you’re generating media on mobile: isolate the DSP, persist the job, and fail the stage that actually broke.
Related work
Apps, packages, and projects this piece connects to.
More writing
Other notes from the archive.

Building Lexora: A Privacy-First Writing Keyboard That Works Offline
Lexora is an Android keyboard that helps people improve spelling, grammar, clarity, and tone wherever they write. Here is how I built its on-device writing engine, custom keyboard, and fast Android integration without sending text to a server.
Oct 2026 · 7 min read

Building iDraw: A Flutter Drawing App With Lessons, a Studio, and an AI Coach
iDraw is a Flutter app I’m building to help people learn to draw — guided lessons, a freehand studio, progress tracking, and an AI coach powered by Google Gemini. Here’s the product brief and the tech behind the strokes.
Sep 2026 · 5 min read

Building a scripting language for hardware panels
Rushabh's lab instruments speak in fixed-width serial strings, and every new panel meant another hardcoded Android screen. So I built a language instead: a lexer, a typed statement tree, an expression evaluator, and buttons you place on a photograph of the machine.
Aug 2026 · 7 min read
Discussion
Questions or notes. Comments live on GitHub Discussions.