Case study
Whisper Subtitle Generator
A speech-to-subtitle service running on one GPU in a flat, with the limits that fact forces on it.
- Role
- Solo project
- Period
- 2026
Context
Whisper transcribes well and formats badly. Raw output arrives as cues broken in the wrong places, at a pace nobody can read, with every proper noun spelled the way it sounded.
The harder problem was not the model, it was exposing it. A public demo backed by one consumer GPU on a domestic connection is a machine a stranger can occupy, so the interesting design work is all in what the service refuses to do.
What I did
A FastAPI service that accepts an upload, queues it, and reports position and progress back, so a wait is something a visitor can see rather than a spinner they have to trust.
Post-processing merges and splits cues to a readable pace, and a terms file corrects the proper nouns the model reliably mishears.
Limits are layered rather than singular: per-request size and duration caps, per-client hourly and daily rates, then global ceilings on queue depth, jobs, transcribed minutes and disk. The global ones persist across restarts, because a limit a process can forget by crashing is not a limit.
Cloudflare Turnstile in front of the upload, verified server side, so the cheap checks reject abuse before anything touches the GPU.
The tunnel routes /api to the Python service and everything else to the Next site under one hostname, which makes the browser same-origin with the API and removes CORS from the design entirely.
What changed
The demo is live rather than a screenshot, which is a different kind of claim to make on a portfolio.
Uploads and generated subtitles are deleted when a job expires, and nothing leaves the machine it was transcribed on.
Built with
- Python
- FastAPI
- Whisper
- PyTorch
- Next.js
- Cloudflare Turnstile