> ## Documentation Index
> Fetch the complete documentation index at: https://docs.runanywhere.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Streaming TTS

> Start speaking before the whole text is synthesized

`synthesizeStream` emits `AudioChunk` values as they are produced, so playback starts on the
first chunk instead of the last.

```ts theme={null}
for await (const chunk of RunAnywhere.tts.synthesizeStream(longText)) {
  player.enqueue(chunk)
}
```

## Signature

```ts theme={null}
synthesizeStream(text: string, options?: TtsOptions): AsyncIterableIterator<AudioChunk>
```

## When it is worth it

For a sentence, `speak` is simpler and the latency difference is invisible. For a paragraph, or
for a model reply being streamed as it generates, streaming synthesis is what keeps the gap
between "the model finished" and "the user hears something" short.

## Pairing it with a streaming completion

Speak each sentence as the model finishes it:

```ts theme={null}
let sentence = ''

for await (const event of RunAnywhere.llm.generateStream(prompt)) {
  if (event.type !== 'textDelta') continue
  sentence += event.text

  if (/[.!?]/.test(event.text)) {
    await RunAnywhere.tts.speak(sentence)
    sentence = ''
  }
}

if (sentence) await RunAnywhere.tts.speak(sentence)
```

For a full conversation rather than one reply, use the [voice session](/electron/voice-agent),
which handles turn-taking and interruption.

## Forwarding chunks to the renderer

When the audio graph lives in the window, send chunks as they arrive rather than buffering the
whole utterance in the main process:

```ts theme={null}
for await (const chunk of RunAnywhere.tts.synthesizeStream(text)) {
  win.webContents.send('audio-chunk', chunk)
}
```

Audio chunks are much larger than text deltas, so batching matters here in a way it does not
for tokens. Send each chunk once and let the renderer schedule them against a running clock.

## Stopping

```ts theme={null}
await RunAnywhere.tts.stop()
```
