ElevenLabs Core Workflow A — TTS & Voice Cloning
Overview
The primary ElevenLabs workflows: (1) Text-to-Speech with voice settings, (2) Instant Voice Cloning from audio samples, and (3) streaming TTS via WebSocket for real-time applications.
Prerequisites
- Completed
elevenlabs-install-auth setup
- Valid API key with sufficient character quota
- For voice cloning: audio recording(s) of the target voice (min 30 seconds, clean audio)
Instructions
Step 1: Advanced Text-to-Speech
import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";
import { createWriteStream } from "fs";
import { Readable } from "stream";
import { pipeline } from "stream/promises";
const client = new ElevenLabsClient();
async function generateSpeech(
text: string,
voiceId: string,
outputPath: string
) {
const audio = await client.textToSpeech.convert(voiceId, {
text,
model_id: "eleven_multilingual_v2",
voice_settings: {
stability: 0.5, // Lower = more expressive, higher = more consistent
similarity_boost: 0.75, // How closely to match the original voice
style: 0.3, // Amplify the speaker's style (adds latency if > 0)
speed: 1.0, // 0.7 to 1.2 range
},
// Optional: enforce language for multilingual model
// language_code: "en", // ISO 639-1
});
await pipeline(Readable.fromWeb(audio as any), createWriteStream(outputPath));
console.log(`Generated: ${outputPath}`);
}
// Generate with different voice settings for comparison
await generateSpeech("Welcome to our platform.", "21m00Tcm4TlvDq8ikWAM", "stable.mp3");
Step 2: Instant Voice Cloning (IVC)
Clone a voice from audio samples using POST /v1/voices/add:
import { createReadStream } from "fs";
async function cloneVoice(
name: string,
description: string,
audioFiles: string[] // Paths to audio samples
) {
const voice = await client.voices.add({
name,
description,
files: audioFiles.map(f => createReadStream(f)),
// Optional: label the voice for organization
labels: JSON.stringify({ accent: "american", age: "young" }),
});
console.log(`Cloned voice created: ${voice.voice_id}`);
console.log(`Name: ${name}`);
// Use the cloned voice immediately
const audio = await client.textToSpeech.convert(voice.voice_id, {
text: "This is my cloned voice speaking!",
model_id: "eleven_multilingual_v2",
voice_settings: {
stability: 0.5,
similarity_boost: 0.85, // Higher for cloned voices to stay close to original
},
});
return { voiceId: voice.voice_id, audio };
}
// Clone from 1-25 audio samples (more = better quality)
await cloneVoice(
"My Custom Voice",
"Professional narrator voice",
["sample1.mp3", "sample2.mp3"]
);
Step 3: WebSocket Streaming TTS
For real-time applications (chatbots, live narration), use the WebSocket endpoint:
import WebSocket from "ws";
async function streamTTSWebSocket(
voiceId: string,
textChunks: string[]
) {
const modelId = "eleven_flash_v2_5"; // Best for real-time streaming
const wsUrl = `wss://api.elevenlabs.io/v1/text-to-speech/${voiceId}/stream-input?model_id=${modelId}`;
const ws = new WebSocket(wsUrl);
const audioChunks: Buffer[] = [];
return new Promise<Buffer>((resolve, reject) => {
ws.on("open", () => {
// Send initial config (BOS - Beginning of Stream)
ws.send(JSON.stringify({
text: " ", // Space signals BOS
voice_settings: {
stability: 0.5,
similarity_boost: 0.75,
},
xi_api_key: process.env.ELEVENLABS_API_KEY,
// How many chars to buffer before generating audio
chunk_length_schedule: [120, 160, 250, 290],
}));
// Stream text chunks
for (const chunk of textChunks) {
ws.send(JSON.stringify({ text: chunk }));
}
// Send EOS (End of Stream)
ws.send(JSON.stringify({ text: "" }));
});
ws.on("message", (data: Buffer) => {
const msg = JSON.parse(data.toString());
if (msg.audio) {
// Base64-encoded audio chunk
audioChunks.push(Buffer.from(msg.audio, "base64"));
}
if (msg.isFinal) {
ws.close();
}
});
ws.on("close", () => resolve(Buffer.concat(audioChunks)));
ws.on("error", reject);
});
}
// Stream from an LLM response in chunks
const chunks = ["Hello, ", "this is ", "streamed ", "speech!"];
const audio = await streamTTSWebSocket("21m00Tcm4TlvDq8ikWAM", chunks);
Step 4: Voice Management
// List all available voices
async function listVoices() {
const { voices } = await client.voices.getAll();
for (const v of voices) {
console.log(`${v.name} (${v.voice_id}) — ${v.category}`);
// category: "premade" | "cloned" | "generated"
}
}
// Get voice settings defaults
async function getVoiceSettings(voiceId: string) {
const settings = await client.voices.getSettings(voiceId);
console.log(`Stability: ${settings.stability}`);
console.log(`Similarity: ${settings.similarity_boost}`);
}
// Update default voice settings
async function updateVoiceSettings(voiceId: string) {
await client.voices.editSettings(voiceId, {
stability: 0.6,
similarity_boost: 0.8,
});
}
// Delete a cloned voice
async function deleteVoice(voiceId: string) {
await client.voices.delete(voiceId);
console.log(`Voice ${voiceId} deleted.`);
}
Voice Cloning Requirements
| Aspect |
Requirement |
| Audio length |
Minimum 30 seconds total (1+ minute recommended) |
| Audio quality |
Clean, no background noise, no music |
| Format |
MP3, WAV, M4A, FLAC, OGG |
| Samples |
1-25 files per voice |
| Languages |
Works across all supported languages |
| Plan |
Available on all paid plans |
Voice Settings Guide
| Setting |
Range |
Low Value Effect |
High Value Effect |
stability |
0-1 |
More expressive, varied |
Consistent, monotone |
similarity_boost |
0-1 |
More creative deviation |
Strictly matches voice |
style |
0-1 |
Neutral delivery |
Exaggerated emotion |
speed |
0.7-1.2 |
Slower speech |
Faster speech |
Recommended starting points:
- Narration: stability=0.5, similarity=0.75, style=0.0
- Conversational: stability=0.4, similarity=0.6, style=0.3
- Cloned voice: stability=0.5, similarity=0.85, style=0.0
Error Handling
| Error |
HTTP |
Cause |
Solution |
voice_not_found |
404 |
Invalid voice_id |
List voices first: GET /v1/voices |
text_too_long |
400 |
Over 5,000 chars per request |
Split text and use previous_text/next_text for prosody |
quota_exceeded |
401 |
Character limit reached |
Check usage, upgrade plan |
too_many_concurrent_requests |
429 |
Exceeds plan concurrency |
Queue requests; see concurrency limits |
invalid_voice_sample |
400 |
Bad audio file for cloning |
Use clean audio, supported format, 30s+ |
WebSocket model_not_supported |
N/A |
eleven_v3 not available for WS |
Use eleven_flash_v2_5 or eleven_multilingual_v2 |
Resources
Next Steps
For speech-to-speech, sound effects, and audio isolation, see elevenlabs-core-workflow-b.
1---2name: elevenlabs-core-workflow-a3description: |4 Implement ElevenLabs text-to-speech and voice cloning workflows.5 Use when building TTS features, cloning voices from audio samples,6 or implementing the primary ElevenLabs money-path: voice generation.7 Trigger: "elevenlabs TTS", "text to speech", "voice cloning elevenlabs",8 "clone a voice", "generate speech", "elevenlabs voice".9allowed-tools: Read, Write, Edit, Bash(npm:*), Bash(curl:*), Grep10version: 1.0.011license: MIT12author: Jeremy Longshore <jeremy@intentsolutions.io>13tags: [saas, voice, ai, elevenlabs, tts, voice-cloning]14compatible-with: claude-code15---1617# ElevenLabs Core Workflow A — TTS & Voice Cloning1819## Overview2021The primary ElevenLabs workflows: (1) Text-to-Speech with voice settings, (2) Instant Voice Cloning from audio samples, and (3) streaming TTS via WebSocket for real-time applications.2223## Prerequisites2425- Completed `elevenlabs-install-auth` setup26- Valid API key with sufficient character quota27- For voice cloning: audio recording(s) of the target voice (min 30 seconds, clean audio)2829## Instructions3031### Step 1: Advanced Text-to-Speech3233```typescript34import { ElevenLabsClient } from "@elevenlabs/elevenlabs-js";35import { createWriteStream } from "fs";36import { Readable } from "stream";37import { pipeline } from "stream/promises";3839const client = new ElevenLabsClient();4041async function generateSpeech(42 text: string,43 voiceId: string,44 outputPath: string45) {46 const audio = await client.textToSpeech.convert(voiceId, {47 text,48 model_id: "eleven_multilingual_v2",49 voice_settings: {50 stability: 0.5, // Lower = more expressive, higher = more consistent51 similarity_boost: 0.75, // How closely to match the original voice52 style: 0.3, // Amplify the speaker's style (adds latency if > 0)53 speed: 1.0, // 0.7 to 1.2 range54 },55 // Optional: enforce language for multilingual model56 // language_code: "en", // ISO 639-157 });5859 await pipeline(Readable.fromWeb(audio as any), createWriteStream(outputPath));60 console.log(`Generated: ${outputPath}`);61}6263// Generate with different voice settings for comparison64await generateSpeech("Welcome to our platform.", "21m00Tcm4TlvDq8ikWAM", "stable.mp3");65```6667### Step 2: Instant Voice Cloning (IVC)6869Clone a voice from audio samples using `POST /v1/voices/add`:7071```typescript72import { createReadStream } from "fs";7374async function cloneVoice(75 name: string,76 description: string,77 audioFiles: string[] // Paths to audio samples78) {79 const voice = await client.voices.add({80 name,81 description,82 files: audioFiles.map(f => createReadStream(f)),83 // Optional: label the voice for organization84 labels: JSON.stringify({ accent: "american", age: "young" }),85 });8687 console.log(`Cloned voice created: ${voice.voice_id}`);88 console.log(`Name: ${name}`);8990 // Use the cloned voice immediately91 const audio = await client.textToSpeech.convert(voice.voice_id, {92 text: "This is my cloned voice speaking!",93 model_id: "eleven_multilingual_v2",94 voice_settings: {95 stability: 0.5,96 similarity_boost: 0.85, // Higher for cloned voices to stay close to original97 },98 });99100 return { voiceId: voice.voice_id, audio };101}102103// Clone from 1-25 audio samples (more = better quality)104await cloneVoice(105 "My Custom Voice",106 "Professional narrator voice",107 ["sample1.mp3", "sample2.mp3"]108);109```110111### Step 3: WebSocket Streaming TTS112113For real-time applications (chatbots, live narration), use the WebSocket endpoint:114115```typescript116import WebSocket from "ws";117118async function streamTTSWebSocket(119 voiceId: string,120 textChunks: string[]121) {122 const modelId = "eleven_flash_v2_5"; // Best for real-time streaming123 const wsUrl = `wss://api.elevenlabs.io/v1/text-to-speech/${voiceId}/stream-input?model_id=${modelId}`;124125 const ws = new WebSocket(wsUrl);126 const audioChunks: Buffer[] = [];127128 return new Promise<Buffer>((resolve, reject) => {129 ws.on("open", () => {130 // Send initial config (BOS - Beginning of Stream)131 ws.send(JSON.stringify({132 text: " ", // Space signals BOS133 voice_settings: {134 stability: 0.5,135 similarity_boost: 0.75,136 },137 xi_api_key: process.env.ELEVENLABS_API_KEY,138 // How many chars to buffer before generating audio139 chunk_length_schedule: [120, 160, 250, 290],140 }));141142 // Stream text chunks143 for (const chunk of textChunks) {144 ws.send(JSON.stringify({ text: chunk }));145 }146147 // Send EOS (End of Stream)148 ws.send(JSON.stringify({ text: "" }));149 });150151 ws.on("message", (data: Buffer) => {152 const msg = JSON.parse(data.toString());153 if (msg.audio) {154 // Base64-encoded audio chunk155 audioChunks.push(Buffer.from(msg.audio, "base64"));156 }157 if (msg.isFinal) {158 ws.close();159 }160 });161162 ws.on("close", () => resolve(Buffer.concat(audioChunks)));163 ws.on("error", reject);164 });165}166167// Stream from an LLM response in chunks168const chunks = ["Hello, ", "this is ", "streamed ", "speech!"];169const audio = await streamTTSWebSocket("21m00Tcm4TlvDq8ikWAM", chunks);170```171172### Step 4: Voice Management173174```typescript175// List all available voices176async function listVoices() {177 const { voices } = await client.voices.getAll();178 for (const v of voices) {179 console.log(`${v.name} (${v.voice_id}) — ${v.category}`);180 // category: "premade" | "cloned" | "generated"181 }182}183184// Get voice settings defaults185async function getVoiceSettings(voiceId: string) {186 const settings = await client.voices.getSettings(voiceId);187 console.log(`Stability: ${settings.stability}`);188 console.log(`Similarity: ${settings.similarity_boost}`);189}190191// Update default voice settings192async function updateVoiceSettings(voiceId: string) {193 await client.voices.editSettings(voiceId, {194 stability: 0.6,195 similarity_boost: 0.8,196 });197}198199// Delete a cloned voice200async function deleteVoice(voiceId: string) {201 await client.voices.delete(voiceId);202 console.log(`Voice ${voiceId} deleted.`);203}204```205206## Voice Cloning Requirements207208| Aspect | Requirement |209|--------|-------------|210| Audio length | Minimum 30 seconds total (1+ minute recommended) |211| Audio quality | Clean, no background noise, no music |212| Format | MP3, WAV, M4A, FLAC, OGG |213| Samples | 1-25 files per voice |214| Languages | Works across all supported languages |215| Plan | Available on all paid plans |216217## Voice Settings Guide218219| Setting | Range | Low Value Effect | High Value Effect |220|---------|-------|-----------------|-------------------|221| `stability` | 0-1 | More expressive, varied | Consistent, monotone |222| `similarity_boost` | 0-1 | More creative deviation | Strictly matches voice |223| `style` | 0-1 | Neutral delivery | Exaggerated emotion |224| `speed` | 0.7-1.2 | Slower speech | Faster speech |225226**Recommended starting points:**227- Narration: stability=0.5, similarity=0.75, style=0.0228- Conversational: stability=0.4, similarity=0.6, style=0.3229- Cloned voice: stability=0.5, similarity=0.85, style=0.0230231## Error Handling232233| Error | HTTP | Cause | Solution |234|-------|------|-------|----------|235| `voice_not_found` | 404 | Invalid voice_id | List voices first: `GET /v1/voices` |236| `text_too_long` | 400 | Over 5,000 chars per request | Split text and use `previous_text`/`next_text` for prosody |237| `quota_exceeded` | 401 | Character limit reached | Check usage, upgrade plan |238| `too_many_concurrent_requests` | 429 | Exceeds plan concurrency | Queue requests; see concurrency limits |239| `invalid_voice_sample` | 400 | Bad audio file for cloning | Use clean audio, supported format, 30s+ |240| WebSocket `model_not_supported` | N/A | eleven_v3 not available for WS | Use `eleven_flash_v2_5` or `eleven_multilingual_v2` |241242## Resources243244- [TTS API Reference](https://elevenlabs.io/docs/api-reference/text-to-speech/convert)245- [Voice Cloning Guide](https://elevenlabs.io/docs/eleven-api/guides/cookbooks/voices/instant-voice-cloning)246- [WebSocket Streaming](https://elevenlabs.io/docs/api-reference/text-to-speech/v-1-text-to-speech-voice-id-stream-input)247- [Voice Settings](https://elevenlabs.io/docs/api-reference/voices/settings/get)248249## Next Steps250251For speech-to-speech, sound effects, and audio isolation, see `elevenlabs-core-workflow-b`.252