What I built
I wanted a workout timer that doesn't just beep at me. So this weekend I built one that writes the workout AND talks me through it, out loud, in a voice that actually sounds like it's yelling at you when things get hard.
You give it a callsign, how long you've got, what you want to work, and how brutal you want it. It hands that to Gemini, which breaks the whole thing into 30-90 second intervals with a coaching line for each one. Then every one of those lines gets turned into real audio by ElevenLabs before it ever hits your browser. Nothing is pre-recorded, nothing is a fixed track. Ask for a different workout, get a completely different script and a completely different set of audio clips, generated on the spot.
Demo
Unedited screen recording, straight off my machine hitting the real APIs, sound included. Compose a routine, it comes back in a couple seconds, pacing curve draws itself as an SVG line, then hitting Start walks through each interval with the active one highlighted in red as it counts down and you actually hear it. The Maximum-intensity segments sound noticeably more unhinged because I turn the ElevenLabs stability knob way down for those specifically.
Code
https://github.com/marwankous/sonic-kinetic
How I built it
Go backend, one endpoint. It takes your workout params, sends a prompt to gemini-3.1-flash-lite with a JSON schema locked down tight enough that I don't have to think about parsing garbage back out of it, and gets back a full timeline plus a heart-rate pacing curve.
The part I actually enjoyed was the audio pipeline. Every coaching line in the timeline gets fired off to ElevenLabs at the same time, one goroutine each behind a sync.WaitGroup, so a routine with a dozen segments doesn't take a dozen times longer than one with a single segment. Whatever comes back gets base64'd straight onto its segment. I also tie the eleven_flash_v2_5 stability setting to the segment's energy level, dropping it to 0.30 for anything marked Maximum, which is a small thing but it's the difference between a calm narrator and something that actually sounds like effort.
Frontend's Angular, standalone components, styled like a terminal because I like terminals. The pacing curve is just a small pure function mapping numbers to an SVG polyline. Playback is a countdown timer per segment that doesn't care how long the audio clip actually is, it plays once and the timer decides when to move to the next row.
Two things broke that I didn't expect. gemini-2.5-flash is apparently dead for new API keys now, so that was a fun 404 to debug at midnight. And ElevenLabs won't let free accounts hit their stock "library" voices over the API anymore, so my first test just kept 402ing until I found a voice from the account's own set that actually worked.
Top comments (0)