Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Optimize ElevenLabs TTS latency with model selection, streaming, caching, and audio format tuning. Use when experiencing slow TTS responses, implementing real-time voice features, or optimizing audio generation throughput. Trigger with "elevenlabs performance", "optimize elevenlabs", "elevenlabs latency", "elevenlabs slow", "fast TTS", "reduce elevenlabs latency", or "TTS streaming".
.claude/skills/jeremylongshore-elevenlabs-performance-tuning/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 17% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 48% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 107% | 0% |
| case-18 | ✗→✓ | ▲ Improved | 19% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 34% | 0% |
Optimize ElevenLabs TTS latency and throughput through model selection, streaming strategies, audio format tuning, and caching. Latency ranges from ~75ms (Flash) to ~500ms (v3) depending on configuration.
The two highest-leverage, lowest-effort levers — model choice (Step 1) and output format (Step 2) — are documented inline below. The four deeper integrations (HTTP streaming, WebSocket streaming, caching, parallel generation) are summarized here with copy-ready code in the full implementation walkthrough.
@elevenlabs/elevenlabs-js)ELEVENLABS_API_KEY (used by the SDK and passed as xi_api_key on the WebSocket handshake)The single biggest performance lever is model choice:
| Model | Avg Latency | Quality | Languages | Use Case | |-------|-------------|---------|-----------|----------| | eleven_flash_v2_5 | ~75ms | Good | 32 | Real-time chat, IVR, gaming | | eleven_turbo_v2_5 | ~150ms | Good | 32 | Balanced speed/quality | | eleven_multilingual_v2 | ~300ms | High | 29 | Narration, content creation | | eleven_v3 | ~500ms | Highest | 70+ | Maximum expressiveness |
typescript// Select model based on use case function selectModel(useCase: "realtime" | "balanced" | "quality" | "max_quality"): string { const models = { realtime: "eleven_flash_v2_5", balanced: "eleven_turbo_v2_5", quality: "eleven_multilingual_v2", max_quality: "eleven_v3", }; return models[useCase]; }
Smaller formats = faster transfer:
| Format | Size/Second | Quality | Best For | |--------|-------------|---------|----------| | mp3_44100_128 | ~16 KB/s | High | Downloads, archival | | mp3_22050_32 | ~4 KB/s | Medium | Streaming, mobile | | pcm_16000 | ~32 KB/s | Raw | Server-side processing | | pcm_44100 | ~88 KB/s | Raw | High-quality processing | | ulaw_8000 | ~8 KB/s | Phone | Telephony/IVR |
typescript// Use smaller format for streaming, higher quality for downloads const streamingConfig = { output_format: "mp3_22050_32", // 4 KB/s — fast streaming model_id: "eleven_flash_v2_5", // ~75ms first byte }; const downloadConfig = { output_format: "mp3_44100_128", // 16 KB/s — high quality model_id: "eleven_multilingual_v2", };
Call client.textToSpeech.stream() instead of .convert() and write each chunk to the response as it arrives, so playback starts before generation finishes — roughly halving time-to-first-byte. Set style: 0.0 in voice_settings to shave another 10–20%. Full server handler: implementation.md § Step 3.
For interactive apps where text arrives incrementally (e.g., an LLM token stream), open a stream-input WebSocket, sendText() chunks as they arrive, and tune chunk_length_schedule — fewer characters per chunk means lower latency but less prosody context. Full bidirectional client: implementation.md § Step 4.
Cache generated audio for repeated content (greetings, prompts, errors) in an LRU cache keyed by a SHA-256 of voiceId:modelId:text, so a changed voice or model never serves stale audio. This eliminates ~99% of latency for repeated phrases. Full cachedTTS helper: implementation.md § Step 5.
Generate multiple segments concurrently with a p-queue whose concurrency matches your plan's request limit (going higher returns 429s, not more throughput). Full chapter-generator: implementation.md § Step 6.
Applying these levers produces:
Time to first byte: 78ms / WebSocket TTFB: 91ms.[Cache HIT] / [Cache MISS] telemetry for repeated content.Expected latency after tuning: ~75–150ms first byte on Flash/Turbo with streaming, versus ~300–500ms for a blocking convert() call on a higher-quality model.
| Optimization | Latency Impact | Implementation | |-------------|----------------|----------------| | Flash model | -60% vs v2, -85% vs v3 | Change model_id | | Streaming endpoint | -50% time-to-first-byte | Use .stream() instead of .convert() | | WebSocket streaming | Best for LLM integration | See Step 4 | | Smaller output format | -30% transfer time | mp3_22050_32 vs mp3_44100_128 | | Audio caching | -99% for repeated content | LRU cache with SHA-256 keys | | style: 0 | -10-20% latency | Remove style exaggeration | | Concurrency queue | Maximize throughput | p-queue matching plan limit |
| Issue | Cause | Solution | |-------|-------|----------| | High TTFB | Wrong model | Switch to eleven_flash_v2_5 | | Choppy streaming | Network buffering | Use pcm_16000 for direct playback | | Cache miss storm | TTL expired for popular content | Use stale-while-revalidate pattern | | WebSocket drops | Network instability | Reconnect with buffered text | | Memory pressure | Audio cache too large | Set maxSize limit on LRU cache | | HTTP 429 | Concurrency above plan limit | Lower p-queue concurrency |
Real-time IVR (lowest latency). Pick eleven_flash_v2_5 + ulaw_8000 via selectModel("realtime"), then stream over HTTP:
typescriptawait streamToResponse(greeting, voiceId, res); // logs "Time to first byte: 78ms"
LLM voice agent (incremental text). Open a WebSocket and forward tokens as they stream from the model, ending with finish():
typescriptconst stream = await createTTSStream({ voiceId, chunkLengthSchedule: [50, 100, 150] }); stream.sendText("Hello, "); stream.sendText("how are you?"); const audio = await stream.finish();
Audiobook batch (throughput). Cache repeated phrases and generate chapters concurrently:
typescriptconst buffers = await generateChapters(chapters, voiceId); // 5-wide, cache-backed
Full, runnable versions of every snippet above are in the implementation walkthrough.
For cost optimization once latency is tuned, see the elevenlabs-cost-tuning skill, which covers character-usage budgeting, model-tier cost tradeoffs, and cache-hit-rate targets.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 17,949 | 17,662 | -2% | 1 | 1 | 0% | 3,890 | 4,556 | +17% | 0 | 0 | — |
case-02 | fail→fail | 22,445 | 30,472 | +36% | 1 | 1 | 0% | 4,626 | 7,155 | +55% | 0 | 0 | — |
case-03 | pass→pass | 17,247 | 18,461 | +7% | 1 | 1 | 0% | 2,776 | 5,492 | +98% | 0 | 0 | — |
case-04 | pass→pass | 23,420 | 17,267 | -26% | 1 | 1 | 0% | 2,373 | 5,263 | +122% | 0 | 0 | — |
case-05 | pass→pass | 17,113 | 14,041 | -18% | 1 | 1 | 0% | 2,496 | 4,383 | +76% | 0 | 0 | — |
case-06 | fail→pass | 14,362 | 4,404 | -69% | 1 | 1 | 0% | 2,008 | 2,971 | +48% | 0 | 0 | — |
case-07 | pass→pass | 10,652 | 6,155 | -42% | 1 | 1 | 0% | 1,573 | 3,037 | +93% | 0 | 0 | — |
case-08 | pass→pass | 17,876 | 6,539 | -63% | 1 | 1 | 0% | 1,443 | 3,053 | +112% | 0 | 0 | — |
case-09 | pass→pass | 8,992 | 4,535 | -50% | 1 | 1 | 0% | 1,697 | 2,969 | +75% | 0 | 0 | — |
case-10 | pass→pass | 9,673 | 6,940 | -28% | 1 | 1 | 0% | 1,797 | 3,495 | +94% | 0 | 0 | — |
case-11 | pass→pass | 16,387 | 11,936 | -27% | 1 | 1 | 0% | 2,518 | 4,250 | +69% | 0 | 0 | — |
case-12 | fail→pass | 8,237 | 5,167 | -37% | 1 | 1 | 0% | 1,398 | 2,898 | +107% | 0 | 0 | — |
case-13 | pass→pass | 5,997 | 3,713 | -38% | 1 | 1 | 0% | 864 | 2,650 | +207% | 0 | 0 | — |
case-14 | pass→pass | 14,364 | 12,092 | -16% | 1 | 1 | 0% | 2,730 | 4,042 | +48% | 0 | 0 | — |
case-15 | pass→pass | 14,477 | 8,457 | -42% | 1 | 1 | 0% | 2,121 | 3,356 | +58% | 0 | 0 | — |
case-16 | pass→pass | 22,037 | 3,608 | -84% | 1 | 1 | 0% | 2,164 | 2,778 | +28% | 0 | 0 | — |
case-17 | pass→pass | 9,380 | 2,572 | -73% | 1 | 1 | 0% | 1,693 | 2,625 | +55% | 0 | 0 | — |
case-18 | fail→pass | 17,919 | 10,945 | -39% | 1 | 1 | 0% | 3,253 | 3,876 | +19% | 0 | 0 | — |
case-19 | fail→pass | 13,531 | 6,315 | -53% | 1 | 1 | 0% | 2,310 | 3,086 | +34% | 0 | 0 | — |
case-20 | fail→pass | 14,269 | 4,100 | -71% | 1 | 1 | 0% | 2,118 | 2,848 | +34% | 0 | 0 | — |
case-21 | pass→pass | 13,018 | 8,465 | -35% | 1 | 1 | 0% | 2,261 | 3,517 | +56% | 0 | 0 | — |
case-22 | pass→pass | 15,131 | 5,812 | -62% | 1 | 1 | 0% | 2,232 | 2,981 | +34% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +27 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.