ElevenLabs
Text-to-speech with ElevenLabsTTSService (multi-context WebSocket), ElevenLabsHttpTTSService (HTTP streaming) and ElevenLabsDialogueTTSService (Text to Dialogue), all with word timestamps.
Overview
ElevenLabs turns text into speech with its Flash, Turbo, Multilingual and v3 models. pipedog ports all three of Pipecat's ElevenLabs text-to-speech services:
ElevenLabsTTSService: streams over ElevenLabs' multi-context WebSocket API (multi-stream-input), with word-level timestamps and per-turn audio contexts.ElevenLabsHttpTTSService: posts each request to thestream/with-timestampsHTTP endpoint and streams the audio and alignment back.ElevenLabsDialogueTTSService: streams over ElevenLabs' multi-context Text to Dialogue WebSocket, which serves the Eleven v3 and v4 dialogue models.
Upstream page
Pipecat's documentation for the same services.
Source
packages/elevenlabs/src/tts.ts, dialogue/tts.ts and tts_base.ts.
ElevenLabs docs
Voices, models and voice settings.
Installation
bun add @pipedog/elevenlabsimport { ElevenLabsDialogueTTSService } from "@pipedog/elevenlabs/dialogue/tts";
import { ElevenLabsHttpTTSService, ElevenLabsTTSService } from "@pipedog/elevenlabs/tts";Prerequisites
- An ElevenLabs account and an API key from the ElevenLabs dashboard.
- A voice id from the ElevenLabs voice library. There is no default voice.
- Pass the key as
apiKey. The examples read it fromELEVENLABS_API_KEY.
ElevenLabsTTSService
Configuration
Prop
Type
Settings
Runtime-updatable settings, passed as settings: new ElevenLabsTTSService.Settings({...}). Any
setting you leave out keeps the default below. null means the field is not sent, so ElevenLabs
uses its own default.
| Setting | Type | Default | Description |
|---|---|---|---|
model | string | "eleven_flash_v2_5" | ElevenLabs model id. |
voice | string | null | null | Voice id. Set one: there is no default. |
language | Language | null | null | Synthesis language. Sent only to models that take a language code. |
stability | number | null | null | Voice stability, 0.0 to 1.0. |
similarityBoost | number | null | null | Similarity boost, 0.0 to 1.0. |
style | number | null | null | Style exaggeration, 0.0 to 1.0. |
useSpeakerBoost | boolean | null | null | Speaker boost. |
speed | number | null | null | Speaking speed, 0.7 to 1.2. |
applyTextNormalization | "auto" | "on" | "off" | null | null | Text normalization mode. |
Usage
import { TTSUpdateSettingsFrame } from "@pipedog/core/frames/frames";
import { TextAggregationMode } from "@pipedog/core/services/tts_service";
import { Language } from "@pipedog/core/transcriptions/language";
import { ElevenLabsHttpTTSService, ElevenLabsTTSService } from "@pipedog/elevenlabs/tts";
const tts = new ElevenLabsTTSService({
apiKey: process.env.ELEVENLABS_API_KEY ?? "",
settings: new ElevenLabsTTSService.Settings({
voice: "your-voice-id",
model: "eleven_flash_v2_5",
language: Language.EN,
stability: 0.5,
similarityBoost: 0.75,
}),
});Token streaming:
// Stream LLM tokens instead of whole sentences. autoMode then defaults to false.
const streaming = new ElevenLabsTTSService({
apiKey: process.env.ELEVENLABS_API_KEY ?? "",
textAggregationMode: TextAggregationMode.TOKEN,
settings: new ElevenLabsTTSService.Settings({ voice: "your-voice-id" }),
});Change settings mid-call with a TTSUpdateSettingsFrame:
// Queue it on the worker, or push it from a processor upstream of the service.
// A voice-character change (stability, speed, ...) starts a new audio context;
// a voice, model or language change reconnects.
const update = new TTSUpdateSettingsFrame({
delta: new ElevenLabsTTSService.Settings({ speed: 1.1 }),
});
Event handlers
Inherited from TTSService:
| Event | Description |
|---|---|
on_connected | Connected to ElevenLabs. |
on_disconnected | Disconnected from ElevenLabs. |
on_connection_error | The connection failed. |
on_tts_request | A text chunk is about to be sent for synthesis. |
Notes
- The service does not push the LLM's text frames itself. It builds
TTSTextFrames from ElevenLabs' word alignment, so the context only records words the user actually heard. - Text is aggregated into sentences by default.
autoModeleftnullistruein sentence mode andfalsein token mode, where ElevenLabs' chunk scheduler needs to gather enough text. - Changing
voice,modelorlanguagereconnects, since they are part of the WebSocket URL. Changingstability,similarityBoost,style,useSpeakerBoostorspeedcloses the current audio context, so the next one opens with the new voice settings. languageis checked against the model's language list. A model that takes no language code, or doesn't cover the language, gets none, and this is logged.- The output format is chosen from the sample rate: 8000, 16000, 22050, 24000, 32000, 44100 or
48000 Hz. Any other rate falls back to
pcm_24000with a warning. pronunciationTransformIpa()renders IPA as SSML<phoneme>tags. Onlyeleven_flash_v2andeleven_turbo_v2read them, and this service also needsenableSsmlParsing: true. Otherwise the transform is skipped.- Other settings changes (such as
applyTextNormalization) are not applied mid-call and are logged as unhandled, unless a URL change reconnects at the same time.
ElevenLabsHttpTTSService
Configuration
Prop
Type
Settings
| Setting | Type | Default | Description |
|---|---|---|---|
model | string | "eleven_flash_v2_5" | ElevenLabs model id. |
voice | string | null | null | Voice id. Set one: there is no default. |
language | Language | null | null | Synthesis language. |
optimizeStreamingLatency | number | null | null | Latency optimization level, 0 to 4. |
stability | number | null | null | Voice stability, 0.0 to 1.0. |
similarityBoost | number | null | null | Similarity boost, 0.0 to 1.0. |
style | number | null | null | Style exaggeration, 0.0 to 1.0. |
useSpeakerBoost | boolean | null | null | Speaker boost. |
speed | number | null | null | Speaking speed, 0.25 to 4.0. |
applyTextNormalization | "auto" | "on" | "off" | null | null | Text normalization mode. |
Usage
const httpTts = new ElevenLabsHttpTTSService({
apiKey: process.env.ELEVENLABS_API_KEY ?? "",
settings: new ElevenLabsHttpTTSService.Settings({
voice: "your-voice-id",
optimizeStreamingLatency: 2,
speed: 1.1,
}),
});Notes
- Word timestamps come from the
with-timestampsendpoint, so this service also emitsTTSTextFrames aligned with playback. - Earlier text in the turn is sent as
previous_text, for continuity between requests. enableLogging: falseturns on ElevenLabs' zero retention mode (enterprise only).- IPA pronunciations (
pronunciationTransformIpa()) work witheleven_flash_v2andeleven_turbo_v2only.
ElevenLabsDialogueTTSService
Configuration
Prop
Type
Settings
Runtime-updatable settings, passed as settings: new ElevenLabsDialogueTTSService.Settings({...}).
| Setting | Type | Default | Description |
|---|---|---|---|
model | string | "eleven_v4_turbo" | An eleven_v3 or eleven_v4 model. Any other model logs a warning. |
voice | string | null | null | Voice id. Registered per context, so a change only opens a new context. |
language | Language | null | null | Synthesis language, sent as language_code where the model supports it. |
stability | number | null | null | Voice stability, 0.0 to 1.0. The only voice setting Text to Dialogue reads. |
A model or language change reconnects the WebSocket. A voice or stability change closes the
current audio context, and the next one registers the new values.
Usage
import { ElevenLabsDialogueTTSService } from "@pipedog/elevenlabs/dialogue/tts";
// Eleven v3 and v4 dialogue models. Needs a voice id.
const dialogue = new ElevenLabsDialogueTTSService({
apiKey: process.env.ELEVENLABS_API_KEY ?? "",
settings: new ElevenLabsDialogueTTSService.Settings({
voice: "your-voice-id",
model: "eleven_v4_turbo",
stability: 0.5,
}),
});Notes
- Text is always aggregated by sentence, and a trailing space is added to each input, because the
server joins inputs verbatim. A
textAggregationModeother thanSENTENCEis ignored with a warning. - Eleven v3 and v4 perform inline audio tags such as
[laughs]. They come back as spoken characters in the alignment, so they reach the LLM context unless a text transform removes them. - A keepalive context is registered when the socket opens, because the server drops a connection
that is idle for 20 seconds and rejects a keepalive that names no registered context. Set a
voiceso it can be registered. pronunciationTransformIpa()renders pronunciations as IPA between slashes, which Eleven v3 reads.seedmakes generation reproducible.
Runtime notes
ElevenLabsTTSServiceuses the globalWebSocketand sends the key as anxi-api-keyheader on the handshake. That works on Bun, Node and Deno, and on Cloudflare Workers, where the socket is opened throughfetch. Upstream sets a 16 MB message size limit; the standardWebSockethas none.ElevenLabsDialogueTTSServiceuses the same WebSocket andxi-api-keyheader asElevenLabsTTSService, so it runs wherever that does.ElevenLabsHttpTTSServiceusesfetch. Upstream's requiredaiohttp_sessionis replaced by an optionalfetchoption (the globalfetchby default).- Not ported: the deprecated
PronunciationDictionaryLocator, theInputParamsclasses and thevoiceId,model,params,pronunciationDictionaryLocatorsandaggregateSentencesconstructor options. UsesettingsandtextAggregationMode.
| Runtime | Works | What it needs |
|---|---|---|
| Bun | ✅ yes | Nothing. |
| Node.js | ✅ yes | Nothing. |
| Deno | ✅ yes | Nothing. |
| Cloudflare Workers | ✅ yes | Nothing. |
Generated from pipecat.runtimes and pipecat.compat in packages/elevenlabs/package.json: ✅ means the compat smoke test passes on that runtime.
pipedog is an unofficial community TypeScript port of Pipecat. It is not affiliated with or endorsed by Daily or the Pipecat project.
Portions adapted from the Pipecat documentation, Copyright (c) 2024–2025 Daily, BSD-2-Clause (see apps/docs/LICENSE-pipecat-docs).