DEV Community

Cover image for Streaming TTS for Developers: Latency, Buffering, and Real-Time Voice Architecture
Smallest AI
Smallest AI

Posted on

Streaming TTS for Developers: Latency, Buffering, and Real-Time Voice Architecture

Most developers first encounter text-to-speech as a simple request-response operation:

  1. Send text.
  2. Wait for synthesis.
  3. Receive an audio file.
  4. Play it.

That model is perfectly reasonable for short prompts, narration, notifications, or anything that can be generated ahead of time.

It becomes much more noticeable when you build conversational AI, real-time IVR, voice assistants, or other systems where the output is created dynamically.

In those applications, the user is not waiting for "audio generation." They are experiencing silence.

That distinction is why streaming TTS matters.

Instead of waiting for the entire response to be synthesized, a streaming pipeline starts delivering audio while synthesis is still happening. Playback can begin before the final part of the response exists.

For developers, the interesting part is not simply that streaming is faster. It changes where latency appears, how you buffer audio, how you connect an LLM to the speech layer, how you handle concurrency, and when caching still beats streaming.

If you are evaluating real-time voice infrastructure, Smallest AI is one example of a platform exposing TTS for these kinds of workloads.

The global text-to-speech market was valued at USD 4.8 billion in 2025 and projected to reach USD 5.7 billion in 2026, according to Global Market Insights. More applications are adding generated speech, but real-time systems have very different architectural requirements from offline voice generation.

What streaming text-to-speech actually means

Traditional batch TTS works as a complete request-response cycle.

You send the full text to the synthesis service. The service generates the complete audio output. Only then does the application receive something it can play.

Conceptually:

Text
  |
  v
TTS synthesis
  |
  v
Complete audio
  |
  v
Playback
Enter fullscreen mode Exit fullscreen mode

Streaming changes the delivery model:

Text
  |
  v
TTS synthesis
  |
  +--> Audio chunk 1 --> Playback starts
  |
  +--> Audio chunk 2
  |
  +--> Audio chunk 3
  |
  +--> ...
Enter fullscreen mode Exit fullscreen mode

The client no longer waits for synthesis to finish before doing useful work.

Depending on the API, streaming can be delivered using mechanisms such as:

  • Server-Sent Events
  • WebSockets
  • Streaming HTTP responses
  • Provider-specific real-time protocols

There are also two different problems that are often grouped under "streaming TTS."

The first is streaming audio output. You already have the complete text, but the TTS service begins returning generated audio before synthesis finishes.

The second is streaming text input and audio output. The input itself is still being generated, often by an LLM, and the TTS pipeline begins synthesizing before the complete LLM response exists.

The second case is especially important for conversational AI.

Streaming TTS is not the same as low-latency TTS

This distinction is easy to miss.

A low-latency batch TTS service might synthesize a short response quickly and return the complete audio file.

A streaming service might take longer to finish the entire synthesis but begin returning playable audio much earlier.

For an interactive application, those are different performance characteristics.

You should usually measure at least:

  • Time to first audio, how long the user waits before hearing anything
  • Total synthesis latency, how long the complete generation takes
  • Real-time factor, whether audio is generated faster than it is consumed
  • Playback underruns, how often playback catches up with generation
  • Tail latency, what happens to slower requests rather than only the average
  • Latency under concurrency, whether performance changes when many sessions run at once

For conversational systems, time to first audio often matters more to perceived responsiveness than the time required to generate the final byte.

That is one reason Smallest AI's text-to-speech product focuses on real-time speech generation alongside conventional TTS workloads.

Why perceived latency is the real UX problem

Human conversations leave very little dead space between turns.

A few hundred milliseconds of pause can feel completely natural. Once additional processing layers start stacking up, however, the experience becomes noticeably less conversational.

A typical voice pipeline may already contain:

User finishes speaking
        |
        v
Speech recognition finalizes
        |
        v
Application / LLM generates response
        |
        v
TTS begins synthesis
        |
        v
Network delivery
        |
        v
Client playback begins
Enter fullscreen mode Exit fullscreen mode

TTS is only one component of the latency budget.

If every stage waits for the previous stage to finish completely, delays accumulate.

Streaming lets you overlap work.

For example, an LLM can continue producing text while TTS synthesizes an earlier sentence. The client can play that sentence while the next audio chunk is still being generated.

Instead of:

LLM completes
      |
      v
TTS completes
      |
      v
Playback
Enter fullscreen mode Exit fullscreen mode

you get something closer to:

LLM tokens ------->

       sentence 1
            |
            v
       TTS chunk 1 --------> playback

              sentence 2
                   |
                   v
              TTS chunk 2 -------->

                     sentence 3
                          |
                          v
                     TTS chunk 3 -------->
Enter fullscreen mode Exit fullscreen mode

That overlap is where much of the perceived latency improvement comes from.

Audio format matters, but streaming is not a codec feature

Streaming is sometimes explained as if only particular audio formats can be streamed.

The actual situation is more nuanced.

Raw PCM is attractive for latency-sensitive pipelines because it has very little decoding or container overhead. Opus is also commonly used for real-time communication because it provides efficient compression and is designed for interactive audio.

MP3 and AAC can also be delivered progressively in appropriate streaming configurations. The tradeoff is that container framing, decoding support, buffering behavior, and browser compatibility can make them less convenient for some ultra-low-latency pipelines.

The correct question is not simply:

"Does this format support streaming?"

Instead, ask:

  • How quickly can the decoder consume the first received bytes?
  • Does each chunk contain enough framing information?
  • Does your playback environment support the codec?
  • How much CPU does decoding require?
  • How much bandwidth does the format save?
  • Are you targeting browsers, telephony, mobile devices, or native applications?

For a voice agent running over telephony, an 8 kHz telephony format may be more useful than high-fidelity audio.

For a browser application, codec and playback API support may become the limiting factor.

Architecture should follow the final listening environment.

The cost equation is not just API pricing

Streaming and batch synthesis are often priced similarly at the model layer.

If an API charges according to characters or generated usage, streaming the response does not necessarily make synthesis itself cheaper.

The infrastructure differences appear elsewhere.

Buffering

With a batch implementation, your service may receive a complete audio payload before forwarding it.

That means larger temporary buffers, especially when outputs are long.

A streaming implementation can forward smaller pieces as they arrive.

At high concurrency, reducing the amount of audio held in application memory can matter.

Connection management

Streaming does not magically eliminate connection costs.

In fact, streaming systems often keep HTTP or WebSocket connections active while audio is being generated or consumed.

That shifts part of your capacity planning toward:

  • Concurrent connections
  • Socket limits
  • Connection lifetime
  • Proxy behavior
  • Load balancer timeouts
  • Backpressure
  • Retry behavior
  • Regional network latency

Caching

This is where batch synthesis can win decisively.

Suppose your application repeatedly says:

Your payment was successful.
Enter fullscreen mode Exit fullscreen mode

There is little reason to synthesize that sentence for every request.

Generate it once, cache it, and serve the existing audio.

Streaming is most valuable when the output is dynamic enough that caching provides little benefit.

If you want a broader benchmark-oriented view of latency and production TTS evaluation, Smallest AI's guide to fast text-to-speech APIs covers the metrics developers should compare.

When streaming TTS is the right architecture

Streaming is a strong fit when the text does not exist until runtime and the user is waiting for the response.

Typical examples include:

  • Conversational AI agents
  • LLM-powered voice assistants
  • Open-ended IVR systems
  • Real-time customer support automation
  • Interactive accessibility applications
  • Dynamic navigation prompts
  • Live narration
  • Generated commentary
  • Applications where speech changes based on user context

The common pattern is interactivity.

The faster useful output begins, the more responsive the system feels.

When batch TTS is still better

Streaming is not a universal replacement for batch synthesis.

Batch is often the simpler architecture when:

  • Content is static
  • Audio can be generated ahead of time
  • The same output will be played repeatedly
  • Total rendering time matters more than time to first audio
  • You are creating podcasts or audiobooks offline
  • Persistent streaming connections add unnecessary complexity
  • Aggressive caching can eliminate repeated synthesis costs

For example, a set of 50 fixed IVR menu prompts should probably not be synthesized live for every caller.

Dynamic answers inside that same IVR may benefit from streaming.

Production systems frequently use both.

How to connect an LLM to streaming TTS

A common real-time pipeline looks like this:

User speech
    |
    v
Speech recognition
    |
    v
LLM token stream
    |
    v
Text buffering
    |
    v
Sentence / phrase boundary
    |
    v
Streaming TTS
    |
    v
Audio queue
    |
    v
Playback
Enter fullscreen mode Exit fullscreen mode

The important step is the buffer between the LLM and TTS.

Sending every individual token directly to synthesis is usually not ideal.

Imagine an LLM producing:

The
weather
tomorrow
will
be
warmer
than
today.
Enter fullscreen mode Exit fullscreen mode

Synthesizing each token independently would destroy natural phrasing.

Instead, accumulate enough text to create a meaningful speech unit:

The weather tomorrow will be warmer than today.
Enter fullscreen mode Exit fullscreen mode

Then send that unit into TTS while the LLM continues generating the next part of the answer.

Sentence boundaries are a latency-quality tradeoff

Waiting for a complete paragraph gives the TTS model more linguistic context, but increases latency.

Sending tiny fragments reduces waiting time, but can damage prosody.

The ideal chunk size depends on:

  • Model behavior
  • Language
  • Punctuation
  • Response length
  • Network latency
  • Conversation style
  • Whether the TTS API maintains context across chunks

A simple implementation might flush the text buffer when it encounters:

  • A period
  • A question mark
  • An exclamation mark
  • A sufficiently long comma-separated phrase

But punctuation alone is not perfect.

Consider:

Dr. Patel will arrive at 4:30 p.m. tomorrow.
Enter fullscreen mode Exit fullscreen mode

Naive splitting can create terrible boundaries.

Production systems often need sentence segmentation that understands abbreviations, numbers, domains, and the language being synthesized.

The key is to measure both latency and listening quality rather than optimizing one in isolation.

Streaming full text and streaming LLM output are different cases

For developers using the Smallest AI speech layer, the current streaming interface distinguishes between these patterns.

When you already have the complete text, an SSE-based request can return audio chunks while synthesis is happening.

When the text itself arrives incrementally, such as an LLM token stream, a persistent WebSocket is the more natural architecture.

That distinction matters because the application controls different forms of backpressure in each case.

With complete text:

Full text -> TTS -> streamed audio
Enter fullscreen mode Exit fullscreen mode

With generated text:

LLM -> text chunks -> TTS connection -> audio chunks
Enter fullscreen mode Exit fullscreen mode

In the second design, your application must decide when a text fragment is ready to synthesize.

Create and store the API key

Keep the API key in an environment variable rather than hard-coding it into the application.

Before running the snippet, create a Smallest.ai API key in the dashboard and store it in the SMALLEST_API_KEY environment variable.

export SMALLEST_API_KEY="your-api-key-here"
Enter fullscreen mode Exit fullscreen mode

Every authenticated request sends the value through the Authorization header:

Authorization: Bearer <SMALLEST_API_KEY value>
Enter fullscreen mode Exit fullscreen mode

Keep the key on your server.

Do not expose it in browser JavaScript, mobile application code, public repositories, screenshots, query parameters, client-side logs, or error messages returned to users.

For production systems, store the secret using your hosting provider's secret-management system or another appropriate server-side secrets manager rather than checking it into configuration files.

Testing streamed TTS with cURL

The following example sends complete text and receives streamed audio events from the TTS endpoint.

Before running the snippet, Create aSmallest.ai API key in the dashboard and store it in the SMALLEST_API_KEY environment variable.

curl --fail-with-body --show-error -N \
  -X POST "https://api.smallest.ai/waves/v1/tts/live" \
  -H "Authorization: Bearer $SMALLEST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Streaming this paragraph chunk by chunk so playback can start sooner.",
    "voice_id": "magnus",
    "sample_rate": 24000,
    "output_format": "pcm"
  }'
Enter fullscreen mode Exit fullscreen mode

The important part from an architecture perspective is not the cURL command itself.

It is that the response can be consumed incrementally rather than waiting for a completed audio file.

For an actual application, your server would parse the stream, decode the audio payload, and forward the appropriate audio data to the client or telephony layer.

The Smallest AI developer platform is the place to create credentials and begin testing the speech layer with your own workload.

Client-side playback needs its own buffer

Receiving audio quickly does not guarantee smooth playback.

Networks are inconsistent.

Imagine the server generates chunks at these times:

chunk 1   100 ms
chunk 2   190 ms
chunk 3   270 ms
chunk 4   610 ms
chunk 5   690 ms
Enter fullscreen mode Exit fullscreen mode

If the client plays everything the moment it arrives, the delay before chunk 4 could create an audible gap.

That is why real-time playback usually maintains a small jitter or playback buffer.

The buffer gives the system enough headroom to absorb short network stalls.

Too small:

lower latency
higher underrun risk
Enter fullscreen mode Exit fullscreen mode

Too large:

higher stability
higher perceived latency
Enter fullscreen mode Exit fullscreen mode

There is no universal correct size.

Measure it under the network conditions your users actually experience.

Browser playback considerations

The browser gives you several possible audio paths.

The Web Audio API gives applications detailed control over audio processing and scheduling.

The Media Source API is useful when your chosen media format and browser support fit the MediaSource model.

For raw PCM, many applications maintain their own queue and schedule decoded audio buffers through Web Audio.

Your client logic needs to handle:

  • Chunk ordering
  • Decoding
  • Playback scheduling
  • Buffer underruns
  • Connection loss
  • Stream completion
  • User interruption
  • Reconnection
  • Cancellation

For browser applications, authenticated TTS requests should still be made by your server. Do not ship your provider API key to the browser simply because the playback component lives there.

A safer flow is:

Browser
   |
   | authenticated application request
   v
Your server
   |
   | provider API credential
   v
TTS API
   |
   | streamed audio
   v
Your server
   |
   v
Browser playback queue
Enter fullscreen mode Exit fullscreen mode

Backpressure matters once everything is streaming

Streaming makes pipelines faster partly because stages can operate concurrently.

It also introduces a new problem.

What happens when one stage is faster than the next?

Suppose the LLM generates text much faster than TTS can synthesize it.

Your queue grows:

LLM
 |
 v
[text][text][text][text][text][text]
                         |
                         v
                        TTS
Enter fullscreen mode Exit fullscreen mode

Now you have increased memory usage and potentially created several seconds of speech that the user has not heard yet.

That makes interruption difficult.

If the user changes direction while five sentences are already queued for speech, the system may need to throw that work away.

A production streaming architecture should define:

  • Maximum queued text
  • Maximum queued audio
  • Cancellation semantics
  • Barge-in behavior
  • Timeouts
  • Retry rules
  • Whether generated but unplayed audio should be discarded

Streaming is not only about moving bytes sooner. It is also about controlling work that is happening concurrently.

Barge-in changes the design again

Conversational voice applications often allow the user to interrupt the assistant.

That means your system may have to cancel:

  1. Audio currently playing
  2. Audio already generated but not played
  3. TTS requests still synthesizing
  4. LLM tokens still being generated

Without coordinated cancellation, the application may stop playback locally while servers continue generating content nobody will use.

The cancellation path should be treated as part of the normal architecture, not an edge case.

A useful mental model is:

User interruption
      |
      +--> stop playback
      |
      +--> clear audio queue
      |
      +--> cancel TTS generation
      |
      +--> cancel or redirect LLM generation
      |
      +--> begin listening again
Enter fullscreen mode Exit fullscreen mode

This is one reason WebSocket-oriented pipelines are common in highly interactive voice applications.

SSML becomes more complicated when text is chunked

The Speech Synthesis Markup Language specification provides controls for things such as pronunciation, pauses, rate, pitch, and emphasis.

In batch synthesis, the TTS system can inspect the complete SSML document before generating speech.

Streaming makes that harder.

A chunk might contain:

<prosody rate="slow">
Enter fullscreen mode Exit fullscreen mode

while the closing tag arrives later.

Whether this works depends on the implementation.

Do not assume a provider's batch SSML behavior is identical to its streaming behavior.

If SSML is important to your application, test:

  • Tags crossing chunk boundaries
  • Pronunciation dictionaries
  • Nested elements
  • Pauses
  • Prosody controls
  • Invalid partial markup
  • Cancellation in the middle of marked-up text

Voice consistency across chunks needs testing

Neural speech generation depends heavily on context.

A sentence synthesized in isolation may not sound exactly the same as that sentence synthesized as part of a paragraph.

Aggressively splitting responses can therefore introduce:

  • Pitch changes
  • Pacing changes
  • Unnatural pauses
  • Repeated intonation patterns
  • Different emotional delivery
  • Audible boundaries between chunks

This is another reason not to treat the smallest possible text chunk as the best possible text chunk.

You are optimizing a multi-dimensional system:

latency
quality
stability
cost
interruptibility
Enter fullscreen mode Exit fullscreen mode

The best production setting is usually a compromise.

Compliance and data handling still apply to streams

Streaming does not reduce your responsibility for the data moving through the system.

For applications handling sensitive information, document:

  • Where input text originates
  • Which service receives it
  • Whether requests are logged
  • Whether audio is retained
  • How traffic is encrypted
  • How long temporary buffers live
  • Which regions process data
  • What happens during retries
  • Which systems can access generated audio

For healthcare workloads containing electronic protected health information, the HIPAA Security Rule requires appropriate safeguards.

Streaming can make the data-flow diagram more complicated because text and audio may pass through several services simultaneously.

That makes explicit architecture documentation even more important.

What to benchmark before choosing a streaming TTS architecture

Do not benchmark a TTS provider using one short sentence from your laptop and call the evaluation finished.

Use your actual workload.

Measure:

  • Time to first audio
  • Median latency
  • P95 and P99 latency
  • Audio generation speed
  • Performance under concurrency
  • Chunk arrival variance
  • Playback underruns
  • Connection failures
  • Retry behavior
  • Long-response consistency
  • Sentence-boundary quality
  • Telephony quality if applicable
  • Performance from your deployment region

Also test the failure path.

What happens when:

  • The TTS connection closes halfway through a sentence?
  • The LLM stops producing tokens?
  • A client disappears?
  • The user interrupts?
  • Your playback queue grows too large?
  • An upstream proxy buffers a supposedly streamed response?
  • The network changes from Wi-Fi to mobile data?

A streaming architecture is only fast if every component preserves streaming behavior.

One buffering reverse proxy can quietly turn your stream back into a batch response.

Streaming or batch: a practical decision framework

Choose streaming TTS when:

  • Output is generated dynamically
  • Users are waiting interactively
  • Time to first audio matters
  • Responses cannot be cached effectively
  • LLM output is incremental
  • The application supports connection and buffer management

Choose batch TTS when:

  • Output is static
  • Content can be pre-generated
  • Audio will be reused
  • Total render time matters more than first audio
  • Simpler infrastructure is valuable
  • Caching materially reduces synthesis volume

Use both when the application contains a mixture of static prompts and dynamic responses.

That hybrid design is common and often more economical than forcing every piece of speech through the same path.

Key takeaways

Streaming TTS is not automatically better than batch synthesis.

It solves a specific problem: getting useful audio to the listener before the complete synthesis job has finished.

For developers building interactive voice systems, the main lessons are:

  • Optimize time to first audio separately from total synthesis time.
  • Overlap LLM generation, speech synthesis, and playback when possible.
  • Do not send individual LLM tokens blindly into TTS.
  • Treat sentence and phrase segmentation as a quality-versus-latency decision.
  • Use buffering to absorb network jitter, but keep the buffer small enough to preserve responsiveness.
  • Plan for backpressure, cancellation, and user interruption.
  • Keep authenticated speech API calls on the server.
  • Test SSML and voice consistency specifically in streaming mode.
  • Use batch synthesis and caching when content is predictable.
  • Benchmark the complete production pipeline rather than the model in isolation.

The difference between a voice application that feels responsive and one that feels sluggish is rarely controlled by a single latency number.

It comes from how the entire pipeline overlaps work.

If you want to test that architecture with your own prompts and audio pipeline, create an API key and start building with the Smallest AI API.

Top comments (0)