Apr 2025 - May 2025
SynthFlow
Text-to-music platform that turns a prompt into a full song with cover art.
SynthFlow is an end-to-end text-to-music platform. A single prompt fans out across audio, language, and image models on a GPU, with the heavy inference decoupled from the request lifecycle so the app stays responsive under load.
Architecture
The problem. One text prompt has to become a full song plus cover art, which means several slow GPU model calls. Run inside the web request, that hangs the app and lets one heavy user starve everyone else.
Decouple inference. The orchestrator does the fast work synchronously (validate, reserve a credit) and hands the slow generation to an Inngest queue with per-user concurrency limits, so the UI stays responsive and no single user monopolizes the GPU.
Correctness under failure. A credit is reserved with an atomic compare-and-decrement so it cannot be double-spent under concurrent requests, and refunded automatically whenever a generation fails.
Closing the loop. Every finished track is genre-tagged by an LLM and dedup-persisted into a catalog that powers the discovery feed.
What I built
- Engineered a text-to-music pipeline orchestrating audio, LLM, and image models on a GPU into a 3-minute song with cover art.
- Decoupled GPU inference from the request lifecycle onto Inngest queues with per-user concurrency limits that prevent starvation.
- Built genre auto-tagging that classifies every track with the LLM and dedup-persists categories that power the discovery feed.
- Designed a credit system that reserves a credit with an atomic compare-and-decrement, refunding it whenever generation fails.
Under the hood
The stack, the data structures, and the decisions that matter, for engineers.
Generation as a durable, per-user-throttled workflow
The request returns immediately after creating a song row and emitting one Inngest event; the work then runs in a background function as a sequence of memoized step.run blocks, so a crash-and-retry resumes from the last completed step rather than re-running model calls. The function sets concurrency { limit: 1, key: userId }, so one user cannot run more than one generation at a time and cannot starve the shared GPU. The long call to the GPU backend uses Inngest's durable step.fetch, so even that survives a worker restart.