Great music from an eclectic library
blended by technology
Midtempo is an automated radio station. There is no playlist and no schedule - a purpose-built system listens to whatever is playing and, in real time, chooses the next track it thinks will sound best alongside it.
It reads the shape of the current song - key, tempo, intensity, mood, the harmonics in the final bars - and looks for another that should follow gracefully, then works out how to blend or sequence the two so you barely notice the join. It doesn't get it right every time, but it's getting there.
Midtempo has no audience tracking, no advertising, and nothing harvested from your visit. The goal is exploring track selection and the transition process, not selling you, or to you.
About
Midtempo, the station and the underlying technology, is a solo non-commercial project. My name is Chris, and this place is a mix of what I do and love.
The station is built to be...
Autonomous - broadcast constantly, without fail, and without oversight.
Elegant - sound like it is carefully curated by a great DJ with a deep understanding of the music they play. It should flow, evolve, and feel both professional and effortless.
Unpredictable - no discernible patterns or awareness of what may play next. That means selections have to happen real-time (so no advance scheduling) with carefully balanced rules; great sounding choices yet relaxed enough for surprises.
It's easy to blend a 120bpm dance track into another 120bpm dance track. It's more difficult mixing dance into swing, or dub into psychedelia. The library includes all of that and more, purposely eclectic to test what's possible.
Midtempo has been the name of a few online and offline projects of mine, all the way back to '95. It fits as a description of my music style too - I was always more comfortable in the Sunday-afternoon slot. Blues, electronic, ambient, soul, hip-hop, swing, folk, dub, R&B, psychedelia, jazz, funk, reggae... if you're here for the music, hello.
The station is free of ads and sponsorship, meaning development - server costs, music license fees, bandwidth - is mostly self-funded. You can support the station here.
Technology
Other automated systems follow a pre-prepared playlist, using a "radio clock" to select tracks according to their playlist category (A, B, golden, high-rotation, etc). Midtempo does something harder: it decides what should play next while the current track is still on air, selecting on what should blend nicely, then reshapes the audio in real time so the two pieces of music meet cleanly. To do that it has three core services:
The Orchestrator
The control centre - the brain that decides what plays next and how. It holds the music library, runs the selection algorithm, and provides the management interface that oversees everything.
- The algorithm scores the next potential track on how nice they might sound (harmonic, tempo, energy, and timbral compatibility), then makes sure it doesn't do the same combinations all the time so the library stays unpredictable.
- It then calculates the crossfade - fade curves, entry gain, tempo ramps, and timings - scores it, and sends detailed instructions to the streaming server.
- Everything overseen by library, track, crossfade, and schedule controls, and all managed with a playground's worth of testing toys.
TypeScript · Express · React · PostgreSQL
The Fingerprinter
The microscope - every uploaded track is trimmed, normalised (same volume, same format), split into a segments, and analysed chunk by chunk to produce the Orchestrator's "fingerprint" data.
- It extracts musical key and Camelot wheel position, tempo and beat grid, loudness, and a weighted intensity rating, focussing on the start and end segments of each track, in half-second increments.
- Interprets mood (happy/sad, relaxed/aggressive, quiet/party) using a Machine Learning model, and adds timbral vectors and beat-aligned harmonics so tracks can be matched on how they actually sound.
- Identifies where the vocals start and end - which drops spoken station idents into the mix, finishing so they never crash over a song's opening lyrics.
Python · Flask · FFmpeg · Essentia
The Streaming Server
The transmitter - and the part that makes the live blending possible. This runs the player on a remote server, inside a server-side browser and captures the result as a broadcast stream.
- Chrome, running headless, loads the Orchestrator's instruction page, which constantly calls home for the next steps. When a new instruction lands, it loads and plays just like a normal webpage might.
- A virtual (pretend) soundcard captures that audio, an encoder packages it and passes through a limiter/compressor (so that the overall volume stays more-or-less constant), and a streaming server makes it available to the world.
- Because playback happens in a browser, volume and tempo can be adjusted live - this is the trick the whole project is built around.
Headless Chrome · Puppeteer · PulseAudio · DarkIce · Icecast
How it fits together
The only manual step is uploading each song, everything else follows auto-magically. The fingerprint service picks it up, analyses, and hands ~1500 different data points back to the Orchestrator. It validates everything and stores the track in the library, and the song is ready to play.
While playing, the Orchestrator asks - in real time - "what should follow this track, and how?". It filters potential candidates through a bunch of scheduling and compatibility rules, then returns a winner alongside a detailed set of mixing instructions.
The browser on the streaming server reads the instructions, loads the next track, and fades, tempo-matches, and triggers the next track, all calculated to the closest millisecond.
Tuning it by ear
In order to work out which rules the algorithm must follow, everything must be configurable, testable, and audible. A set of tools in the management system provide over 75 different selection settings. I can test these in a separate development "sandbox", which can generate weeks of simulated playlists. These can then be speed-listened (i.e. skipping the middle of songs so that I just hear transitions), and the overall shape reviewed before pushing any configuration changes live.
To make it difficult, there's a huge range of genres, tempos, keys, eras, and countries represented in the library. Odd tempos from Congo band Kokoko! will always be tricky. Sometimes the decision not to blend, or blend very briefly, is the right one.
The biggest challenge is walking the line between variety and mix-quality. I could direct the algorithm to focus only on perfect combinations, but that'd be at the expense of the unexpected. For me, it's in those middle-spaces where interesting things happen - and data on what's-not-great is still valuable. Unfortunately this means there will be the occasional car-crash, and I'm okay with that... for now.
The Presenter
Blending two very different songs is the hard part, and the algorithm doesn't always land it cleanly. When it looks at a join and isn't confident, it can either go back for a different track, or try to cover it up.
The Presenter is mostly here because text-to-speech is my current work focus, so this is all good learning. I've still approached it with the same rigour as everything else. Exactly like a radio presenter talking across the gap between two records, this drops a short spoken station ident over the join. It's triggered to cover those transitions the algorithm is unsure of, turning what might be a slightly awkward mix into something that sounds radio-ish (and maybe elevating this place to something more than a glorified Sp*tify playlist).
It works in two steps. First, a language model writes a short script for that exact moment - the station name, the track just finishing, what's coming next, the time of day - kept deliberately brief, a line or two at most. Then a text-to-speech voice reads it aloud, in a chosen character and pace.
The result is a short piece of audio, timed for precise length. The system drops the clip over the crossfade and lowers ("ducks") the music underneath the voice, hopefully finishing just before the next song's vocals begin so the "presenter" never talks over the lyrics - the Fingerprinter has an advanced vocal detection process for this very reason.
The voice only appears when it's needed, and everything is tunable - trigger thresholds, the voice, the pace, the text-to-speech model - with a whole set of presenter "personas" that can be matched to the music's mood/tempo/time of day (another fun toy).
Infrastructure & hosting
The whole thing runs in the cloud, self-funded and kept deliberately lean. Each service is hosted to match its shape:
- The Stream and the Orchestrator share one small VM (virtual machine). A single connection is held open permanently to provide the streaming endpoint. Because there's some spare capacity, the Orchestrator is co-located at no extra cost. The live audio stream always wins under load (CPU-priority weighting), and co-location means communication between the two core services is invisible and unbreakable.
- The Fingerprinter and Presenter run serverless, on demand. Audio analysis is heavy work but occasional, so these systems sit quietly, idle and costing nothing, until a track needs processing or an ident needs generation. Then it spins up with several gigabytes of memory, does the work, and shuts back down. During standard operation this will be barely enough to trip any cost thresholds [yay to mostly free!]
- Data and audio stay private. The library lives in a managed database reached only by the Orchestrator, and the audio files are in private storage that can only be read by the streaming service and the management UI.
- Structure - all built using a multi-Docker environment (I can spin up, run, and develop everything locally before pushing); unit, end-to-end, and mutation testing (>90% coverage - some 6.5k unit tests for the Orchestrator alone); changes deployable by typing a single command. Working on this is - and must continue to be - a joy.
Finally...
What you're hearing has evolved from a lifetime of music collecting, a career designing systems and writing code, months of manual track processing, and over five years of station test and learn.
Building a dynamic streaming service with real-time audio manipulation and a sophisticated sequencing / blending algorithm has been challenging - as are my future-plans for the station. But doing challenging things makes me happy, which I guess is the whole point of everything.
Unfortunately all this comes at a cost, which grows as more people stumble across it. Without wanting to sound like a stuck record, if you end up spending any time here, any financial support is most welcome.
Contact
Any questions, please email me at chris@midtempo.net.
I post (very) occasionally on Mastodon, or LinkedIn for the more business minded.