
Weekend Creative Challenge: The Midnight Station
A radio station that doesn't exist, broadcasting to almost nobody. Tune the dial to anything at all and it writes a segment and reads it to you out loud — Amazon Bedrock writes, Amazon Polly speaks.
Live app: http://midnight-station-846719029074.s3-website-us-east-1.amazonaws.com/
Source: https://github.com/Prakhar2025/midnight-station
Vision & what the app does
The Midnight Station is a radio station that doesn't exist. It broadcasts between 2am and 4am to almost nobody, from a transmitter that isn't there.
You tune the dial to anything at all — "the swimming pool after closing", "a town where it always rains indoors", "the sound of a fridge at 3am" — pick a segment and a presenter, and the station comes on air and reads it to you out loud.
There are four segments, which are the four things a radio station does when nobody's listening:
- The Forecast — a weather report for a place that doesn't exist, describing impossible weather in a completely routine tone
- The Dedication — a listener has called in to dedicate a song to someone. The caller, the song and the band are all invented
- The Advert — an ad for a product that should not exist, with unsettling small print
- The Sign-Off — the presenter closing the broadcast and going home
Six Amazon Polly neural voices play the presenter. While the audio plays, an ON AIR sign lights up and a 48-bar VU meter moves in time with the actual waveform — it's driven by a real Web Audio analyser reading the frequency data, not a looping animation.
The thing I wanted to avoid was another box that prints text at you. The output here is a voice. You don't read the forecast for Liquida Falls, you sit and listen to someone tell you the ocean will evaporate at midnight. That changes it from a demo into something with atmosphere.
How I built it
One Lambda, one HTML file, no build step and no framework. The whole app is two files.
The request flow is a chain of two AI services. Bedrock's Nova Micro writes the script, then the same Lambda hands that script to Polly, which returns MP3 audio. I base64 the audio into the JSON response so the browser can play it straight from a data URI — no second request, no S3 write, no presigned URL, nothing to clean up afterwards. A 60-word segment comes back at around 150 KB, which is comfortably inside API Gateway's payload limit.
The pivot. I started out wanting to generate images with Amazon Nova Canvas. It refused with
ResourceNotFoundException: This model is marked by provider as Legacy . Listing image-capable models showed only Stability's editing models — upscale, inpaint, background removal — no plain text-to-image on my account. Rather than wait on model access, I checked what was already enabled and found Amazon Polly wide open, with neural voices and a 5M-character free tier. Losing images turned out to be the best thing that happened to this project: audio is a far more distinctive output, and it cost nothing.Writing for a voice is not writing for a screen. This was the real lesson. My first outputs were unusable when spoken, because the model kept emitting stage directions —
[sound of rain] , [pause] — and Polly reads those aloud, brackets and all. Two fixes: the system prompt now explicitly says write only the words to be spoken aloud, never use square brackets, and the Lambda strips anything in brackets with a regex as a backstop. Prompt plus safety net, because a model will eventually ignore any single instruction.Polly's default delivery was too brisk for a 3am presenter. Wrapping the script in SSML with
<prosody rate="92%"> and injecting a <break time="700ms"/> at every paragraph break was the difference between a text-to-speech demo and something that sounds like radio. It's about four lines of code and it's the single highest-impact change in the project.Autoplay. Browsers block audio until the user has interacted with the page. I let
.play() fail silently and put a Replay button in the card, so the audio always has a way to start.If Polly fails, the Lambda still returns the script and hides the VU meter. Losing the voice shouldn't lose the writing.
AWS services used / architecture overview
Plain text
Browser │ POST { signal, segment, voice } ▼Amazon S3 ── static website hosting (index.html, no build step) │ ▼Amazon API Gateway ── HTTP API, CORS, $default route │ ▼AWS Lambda ── Python 3.12, 512 MB, 30s │ ├─► Amazon Bedrock ──► Nova Micro (writes the script) │ └─► Amazon Polly ──► neural voice (speaks the script) │ returns { title, script, audio: base64 mp3 } ▼Amazon CloudWatch Logs
Service | Role |
|---|---|
Amazon Bedrock | Script generation via the Converse API |
Amazon Nova Micro | The writer — fast and cheap |
Amazon Polly | Neural text-to-speech, six presenter voices, SSML pacing |
AWS Lambda | Orchestrates Bedrock → Polly, cleans output, base64-encodes audio |
Amazon API Gateway | Public HTTPS endpoint + CORS |
Amazon S3 | Static website hosting |
AWS IAM | Execution role scoped to Bedrock invoke + polly:SynthesizeSpeech |
What I learned
Chaining two AI services in one Lambda is easy, and the seam is where the work is. Calling Bedrock is simple. Calling Polly is simple. Making Bedrock's output safe to speak — no brackets, no markdown, no asterisks — was the actual engineering, and none of it is in the API docs.
SSML is underrated. A rate change and a pause tag did more for the finished feel than anything else I wrote.
Check what's actually enabled before you design. I lost time architecting around an image model I couldn't call.
list-foundation-models and one test invoke would have told me in thirty seconds.Degrade, don't fail. The Polly call sits in its own try/except. If the voice dies, the words survive. That's three lines and it means a partial outage still ships something.
Audio is a wide-open space in these challenges. Nearly everything built this weekend renders text to a screen. The moment a project makes a sound, it stops being another AI demo.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article