AI System Design

Design a Speech-to-Text Service

Step 1 / 9

Learn AI system design by building a speech-to-text / ASR service like Whisper-at-scale, Deepgram or Google Speech step by step.

The numbers to beatstreamingpersistent conn, partialsbatchasync job + callbackshared fleetone model, two paths

In the interview room

How you’d open this design in an interview

Before any boxes: agree what it must do, pin the qualities that shape everything, then build — naming each trade-off as you make it. The walkthrough above is that exact order.

Functional requirements

What it must do — agree on these before drawing a single box.

  • Batch transcribe: turn an uploaded recording into an accurate transcript as an async job.
  • Stream transcribe: caption live audio, emitting words within a fraction of a second as they’re spoken.
  • Run the model: convert a waveform to a mel spectrogram and decode it to text tokens on a GPU.
  • Segment: use VAD to split audio at pauses with overlapping windows, decoding streams incrementally.
  • Post-process: add punctuation, word timestamps, speaker diarization and custom vocabulary.

Non-functional requirements

The qualities that shape the whole design — each one names the mechanism that buys it.

Serve accuracy-first and real-time from one model
Recognize batch and streaming as opposite constraints and split them — an async job for batch, an incremental low-latency connection for streaming — sharing the model.
A batch flood can’t starve real-time
Route batch uploads through a job queue on a path separate from streaming (reserved capacity/priority), so bursts absorb as backlog without touching the interactive lane.
Feed a fixed model window well
VAD splits audio at natural pauses with overlapping windows and streaming decodes chunks incrementally into revisable partials — chunk size trades latency vs accuracy.
Run expensive GPUs economically
Batch many requests per GPU on the batch path but keep streaming batches small and prompt-flushed; autoscale on load and keep weights warm to avoid cold starts.
Raw tokens become a usable product
A post-processing layer restores punctuation and capitalization, adds timestamps, speaker diarization, language ID and custom vocab — a distinct value layer that degrades gracefully.
Hold the budget under noise + long audio
Small chunks + fast/quantized models + partial-then-refine for latency, denoise/robust models for noise, and a sliding window with stitching for long-form recordings.

The trade-offs you say out loud

Senior signal isn’t the boxes — it’s naming what you gave up and why it was the right price.

Split batch and streaming at the front doorover one naive upload → transcribe → return design

Batch is accuracy-first and latency-tolerant; streaming must emit words within a fraction of a second before the sentence ends. Opposite priorities need different plumbing — async job vs low-latency incremental connection — even sharing the model.

Separate queues / reserved capacityover one undifferentiated GPU pool

Batch and streaming compete for the same scarce GPUs but have incompatible SLAs; a shared pool lets a batch flood ruin real-time latency. Isolation protects the interactive path.

VAD + overlapping windows + partialsover fixed 1-second slices with hard boundaries

Hard fixed cuts slice words in half and lose context that spans a boundary, hurting accuracy. VAD cuts at natural pauses, overlap carries context across, and revisable partials give the live-caption feel.

Large batches for batch, tiny for streamingover a single batch size across both paths

One batch size forces a bad compromise — large enough for throughput makes streaming wait for a batch to fill (killing latency); small enough for streaming wastes GPU efficiency batch doesn’t need to lose. Size batching to each path’s SLA.

Small chunks + fast/quantized model on streamingover the full-size accuracy-first model everywhere

The streaming path is latency-dominated, so a slightly smaller/faster model and small chunks hold the sub-second budget; the batch path keeps the big model where accuracy matters and nothing waits on a fast response.

What this teaches

Learn AI system design by building a speech-to-text / ASR service like Whisper-at-scale, Deepgram or Google Speech step by step. An interactive guide covering batch vs streaming transcription, the ASR inference pipeline, chunking and voice activity detection, a batched autoscaling GPU fleet, post-processing (punctuation, diarization, timestamps), and the unhappy paths (real-time latency budget, accuracy vs chunk size, noisy audio, cost).

Key takeaways

  • Batch (accuracy, latency-tolerant) and streaming (real-time, low-latency) are opposite constraints — split them.
  • A gateway routes streaming to a persistent connection and batch to an async job queue, sharing one GPU fleet.
  • Keep batch and streaming on isolated paths so a batch flood can't starve real-time latency.
  • The model turns a mel spectrogram into text tokens — GPU-bound seq2seq inference.
  • VAD + overlapping chunks + incremental partials feed the fixed model window and trade latency vs accuracy.
  • Batch on GPUs for throughput but keep streaming batches small; autoscale and keep weights warm.
  • Post-processing (punctuation, timestamps, diarization, custom vocab) turns raw tokens into a usable transcript.

Concepts covered

  • What makes speech-to-text hard?
  • Batch vs streaming
  • A gateway that splits the paths
  • Async jobs for uploads
  • The ASR inference pipeline
  • Chunking & voice activity detection
  • Batched, autoscaling GPU workers
  • Post-processing
  • Latency, noise & cost

Design a Speech-to-Text Service — read the full walkthrough as text

the same steps, decisions & trade-offs, for reading, reference & search

The big idea

What makes speech-to-text hard?

Turning audio into text splits into two very different products. Batch: transcribe an uploaded hour-long recording accurately (podcasts, meetings) — accuracy matters, latency doesn't. Streaming: caption a live conversation as it's spoken, showing words within a fraction of a second — latency is everything. Same model, opposite constraints, and both run heavy GPU inference.

A speech-to-text (ASR) service serves both modes over a GPU model. The shape: a gateway that splits streaming from batch, a batched GPU worker fleet running the model, chunking/VAD to feed it, and post-processing (punctuation, timestamps, diarization) to make raw tokens useful. The hard parts are the streaming latency budget and cost.

How to read this: Each step opens with a real design decision — make the call before I show you what ships. Watch the pipeline grow, and at the end flood the batch queue and disable post-processing to see path isolation and the value layer. Hit Begin.

Step 1 · Two modes, one model

Batch vs streaming

Before any architecture, recognize that "transcribe audio" is really two problems with opposite priorities. What are they, and why can't one design serve both naively?

Design decision: Why can't a single naive "upload → transcribe → return" design serve all speech-to-text?

The call: Because audio files are too large to upload. — File size is manageable. The real split is latency: real-time captioning can't wait for a whole-file transcription, so it needs incremental, low-latency processing that a simple upload-and-return can't provide.

Two modes, opposite constraints. Batch can wait and use the whole recording for maximum accuracy — an async job. Streaming must emit words as they're spoken, within a fraction of a second, before the sentence is even done — a persistent connection with incremental, low-latency decoding. They share the model but need different plumbing, so the design splits them at the front door.

Latency defines the shape: Batch = throughput/accuracy-optimized async jobs (like the text-to-image queue). Streaming = latency-optimized real-time, emitting partial transcripts that may be revised as more audio arrives. Recognizing these as separate paths (sharing model + fleet) is the key first decision.

Step 2 · The front door

A gateway that splits the paths

Given two modes, what does the entry point do the moment audio (live or uploaded) arrives?

A gateway routes by mode. For streaming, it holds a persistent connection (WebSocket/gRPC) and pipes audio frames toward the workers, returning partial transcripts live. For batch, it accepts the file, enqueues an async job, and returns a job id to poll or a callback URL. Both feed the same GPU model fleet, but through different flow-control: real-time streaming vs buffered async jobs.

One fleet, two front-ends: The gateway is where the two constraints are reconciled: it exposes a low-latency streaming interface and a throughput-oriented batch interface, then multiplexes both onto a shared, expensive GPU fleet. Keeping the interfaces separate lets each meet its own SLA.

Step 3 · Buffer the batch

Async jobs for uploads

Uploaded recordings are heavy (an hour of audio is minutes of GPU work) and arrive in unpredictable bursts. How do you handle them without dropping jobs or starving the latency-sensitive streaming path?

Send batch uploads through a job queue to the GPU workers, exactly like any heavy async job: enqueue, transcribe at the fleet's sustainable rate, deliver via poll or callback. Crucially, keep batch and streaming on separate paths/queues (or reserved capacity) so a flood of uploaded files can't consume the GPUs that real-time captioning depends on. Batch absorbs bursts as backlog; streaming keeps its low-latency lane.

Isolate the SLAs: Batch and streaming compete for the same scarce GPUs but have incompatible SLAs. A shared undifferentiated pool lets a batch flood ruin real-time latency. Separate queues (or capacity reservations / priority) protect the interactive path — the same isolation principle as any mixed-priority workload.

Step 4 · Inside the model

The ASR inference pipeline

A worker has audio to transcribe — what does the model actually do to turn a waveform into words?

Convert the raw waveform into audio features — typically a mel spectrogram (a time-frequency representation) — then feed it to the ASR model (a Whisper-style encoder-decoder or transducer), which produces a sequence of text tokens, decoded (often with beam search and an optional language model) into words. It's a full neural-network inference per segment of audio, which is why it's GPU-bound. Modern models also do language ID and are robust to accents/noise as part of the same pass.

Waveform → features → tokens: ASR is sequence-to-sequence: audio in, text out. The mel spectrogram is the standard input representation; the model attends over it to emit tokens. Compute scales with audio length and model size, so how you segment the audio (next step) directly affects both latency and cost.

Step 5 · Feeding the model

Chunking & voice activity detection

You can't feed an open-ended live stream (or a one-hour file) to the model in one shot — models have a bounded input window, and streaming needs to emit results before the audio ends. How do you segment the audio well?

Design decision: You can't feed unbounded/live audio to a fixed-window model in one shot. How do you segment it?

The call: Use voice-activity detection to split at pauses, with overlapping windows, and decode chunks incrementally. — VAD finds speech vs silence so you can segment at natural pauses (not mid-word), overlap windows so context spans boundaries, and — for streaming — decode each chunk as it arrives, emitting partial transcripts that may be refined as more audio comes in. This balances the model's bounded window against accuracy and latency.

Use voice-activity detection (VAD) to split audio into speech segments at natural pauses (not mid-word), with overlapping windows so context carries across boundaries. For streaming, decode each chunk incrementally as it arrives and emit a partial transcript that can be revised as more audio comes in; finalize when the segment ends. Chunk size is the core dial: smaller = lower latency but less context (lower accuracy), larger = the reverse.

VAD + overlap + partials: Segmentation is where streaming ASR lives or dies. VAD avoids cutting words; overlap preserves cross-boundary context; incremental decoding with revisable partials gives the "words appear as you speak, then settle" experience. The chunk-size knob directly trades latency against accuracy.

Step 6 · Feed the fleet

Batched, autoscaling GPU workers

ASR inference is GPU-heavy, demand fluctuates, and you're paying for expensive accelerators. How do you run the worker fleet economically — while respecting streaming's latency?

Design decision: The same GPU fleet serves both batch and streaming. Should both paths use the same batch size for inference?

The call: No — large batches for the batch path, small/prompt-flushed batches for streaming. — Batching raises throughput but adds latency by design (waiting to fill the batch). Batch jobs are latency-tolerant so they can use large batches for cost efficiency; streaming needs tiny, immediately-flushed batches to keep the sub-second budget, accepting lower GPU efficiency as the price.

Batch multiple requests per GPU to raise utilization (with care: streaming needs small, fast batches to stay low-latency, while batch jobs can use large batches for throughput). Autoscale the fleet on load (queue depth for batch, active-stream count for real-time). Keep the multi-GB model resident (warm pool, avoid cold starts), and consider smaller/faster models or quantization for the streaming path where latency dominates and a slight accuracy trade is acceptable.

Batch for throughput, small for latency: Same GPU-economics as any inference service, with a twist: batching helps throughput but hurts latency, so you use big batches for the batch path and tiny, prompt-flushed batches for streaming. Autoscaling and warm weights keep costs down without breaking the real-time SLA.

Step 7 · Make it useful

Post-processing

The model outputs raw tokens — often lower-cased, unpunctuated, with no idea who spoke or when. That's a wall of text, not a usable transcript. What turns tokens into a product?

Add a post-processing layer: punctuation & capitalization restoration, word-level timestamps (for captions/search), speaker diarization (who spoke which segment), language identification, and optional custom vocabulary / boosting for domain terms and names. This runs after (or alongside) decoding and is what makes a transcript readable, searchable, and attributable. It's a distinct value layer that can degrade gracefully if it fails.

Raw tokens → a transcript: ASR gives you words; the product is punctuated, time-aligned, speaker-attributed text. Diarization ("who spoke") and timestamps are separate models/steps composed on top of recognition. Custom vocab fixes the domain-specific names and jargon that generic models mishear.

Step 8 · The sharp edges

Latency, noise & cost

Real ASR fights hard problems: the streaming latency budget, noisy/overlapping/accented audio, long-form recordings that exceed the model window, and GPU cost.

Hold the streaming latency budget with small chunks, fast/quantized models, and partial-then-refine output — accepting a small accuracy trade for responsiveness. Improve noisy audio with denoising/robust models and VAD, and handle overlapping speakers with diarization. Transcribe long-form audio by sliding the model window with overlap and stitching results. Control cost with batching, right-sized models per path, and caching/dedup of repeated audio. Fail gracefully — degrade post-processing before dropping the core transcript.

Design for the unhappy path: Real-time budget → small chunks + fast models + revisable partials. Noise/overlap → robust models, denoise, diarize. Long audio → sliding window + stitch. Cost → batch, right-size, cache. Streaming ASR is a constant negotiation between latency and accuracy; the knobs above are how you tune it per product.

You did it

You just designed a speech-to-text service.

  • Batch (accuracy, latency-tolerant) and streaming (real-time, low-latency) are opposite constraints — split them.
  • A gateway routes streaming to a persistent connection and batch to an async job queue, sharing one GPU fleet.
  • Keep batch and streaming on isolated paths so a batch flood can't starve real-time latency.
  • The model turns a mel spectrogram into text tokens — GPU-bound seq2seq inference.
  • VAD + overlapping chunks + incremental partials feed the fixed model window and trade latency vs accuracy.
  • Batch on GPUs for throughput but keep streaming batches small; autoscale and keep weights warm.
  • Post-processing (punctuation, timestamps, diarization, custom vocab) turns raw tokens into a usable transcript.
built to turn a stream of sound into words as they are spoken — make the calls, flood the queue, run the gauntlet.
Finished this one? 0 / 61 AI System Designs done

Explore the topic

See this alongside everything else on the same subject — handbooks, system designs, challenges and tools, in one place.

More AI System Designs