Generez des voix naturelles avec ElevenLabs : API Text-to-Speech, reglages stability et similarity, streaming faible latence, pipeline vocal LLM, couts et ethique du clonage.
Le TTS moderne en bref
La synthese vocale a fait un bond spectaculaire. Les voix robotiques d'antan ont cede la place a des voix naturelles, capables de respirer, d'accentuer, de transmettre une emotion. ElevenLabs s'est impose comme la reference, avec un rendu multilingue convaincant et une API simple.
Le TTS est le pendant de Whisper : la ou Whisper transcrit la voix en texte, le TTS genere de la voix a partir de texte. Ensemble, ils forment la base des assistants vocaux, des livres audio automatises et de l'accessibilite.
Prerequis et cle API
- Compte ElevenLabs et cle API
- Node.js 18+ (support natif de
fetchet streams) - Cle stockee dans
ELEVENLABS_API_KEY
# Installer le SDK officiel
npm install @elevenlabs/elevenlabs-js
# Stocker la cle (jamais commitee)
echo "ELEVENLABS_API_KEY=..." >> .env
Premiere generation audio
Generons un fichier audio a partir d'un texte. L'API renvoie un flux audio que l'on ecrit sur disque.
// tts-first.js — generer un MP3 a partir de texte
import 'dotenv/config';
import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js';
import { writeFile } from 'node:fs/promises';
const client = new ElevenLabsClient({ apiKey: process.env.ELEVENLABS_API_KEY });
// Generer l'audio (renvoie un flux de chunks)
const audioStream = await client.textToSpeech.convert('voice-id-ici', {
text: 'Bonjour et bienvenue sur AngularForAll, votre ressource technique.',
modelId: 'eleven_multilingual_v2', // modele multilingue (gere le francais)
outputFormat: 'mp3_44100_128', // qualite MP3 44.1kHz 128kbps
});
// Accumuler les chunks et ecrire le fichier
const chunks = [];
for await (const chunk of audioStream) chunks.push(chunk);
await writeFile('bienvenue.mp3', Buffer.concat(chunks));
console.log('Audio genere : bienvenue.mp3');
eleven_multilingual_v2 gere de nombreuses langues dont le francais, avec detection automatique. Pour de l'anglais pur a faible latence, des modeles « turbo » plus rapides existent.
Regler la voix : stability et similarity
ElevenLabs expose des reglages qui modulent le rendu. Les deux principaux sont stability (constance vs expressivite) et similarity_boost (fidelite a la voix d'origine).
// Affiner les parametres de voix
const audioStream = await client.textToSpeech.convert('voice-id-ici', {
text: 'Cette phrase sera lue avec une intonation expressive.',
modelId: 'eleven_multilingual_v2',
voiceSettings: {
stability: 0.35, // bas = plus expressif/variable ; haut = monotone/stable
similarityBoost: 0.8, // fidelite a la voix de reference
style: 0.4, // intensite stylistique (selon le modele)
useSpeakerBoost: true, // renforce la presence de la voix
},
});
| Parametre | Valeur basse | Valeur haute |
|---|---|---|
| stability | Expressif, variable | Stable, monotone |
| similarity_boost | Plus libre | Fidele a la reference |
| style | Neutre | Marque, theatral |
stability moyenne (0.4 a 0.6) evite les variations excessives. Pour un personnage expressif, descendez plus bas. Testez sur une phrase representative avant de generer un long contenu.
Streaming audio faible latence
Attendre la generation complete d'un long texte introduit une latence penible. Le streaming renvoie l'audio des les premiers mots — indispensable pour un assistant vocal.
// tts-stream.js — relayer l'audio en streaming vers le navigateur
import express from 'express';
import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js';
const app = express();
const client = new ElevenLabsClient({ apiKey: process.env.ELEVENLABS_API_KEY });
app.get('/speak', async (req, res) => {
res.setHeader('Content-Type', 'audio/mpeg');
// stream() renvoie les chunks au fur et a mesure de la generation
const audioStream = await client.textToSpeech.stream('voice-id-ici', {
text: req.query.text,
modelId: 'eleven_turbo_v2_5', // modele turbo = latence reduite
outputFormat: 'mp3_44100_128',
});
// Pipe direct vers la reponse HTTP : le navigateur joue en streaming
for await (const chunk of audioStream) {
res.write(chunk);
}
res.end();
});
app.listen(3000);
// Cote navigateur : jouer le flux audio
const audio = new Audio('/speak?text=' + encodeURIComponent('Bonjour !'));
audio.play(); // commence des reception des premiers chunks
Lister et choisir les voix
ElevenLabs fournit une bibliotheque de voix pretes a l'emploi. Recuperez la liste pour choisir le voice_id adapte a votre cas.
// Lister les voix disponibles et leurs metadonnees
const voices = await client.voices.getAll();
voices.voices.forEach((v) => {
console.log(`${v.name} (${v.voiceId})`);
console.log(` Langue/style : ${v.labels?.accent ?? '-'}, ${v.labels?.gender ?? '-'}`);
});
// Choisir programmatiquement une voix selon des criteres
function pickVoice(voices, { gender, accent }) {
return voices.find((v) =>
v.labels?.gender === gender && v.labels?.accent === accent
) ?? voices[0];
}
voice_id en configuration plutot qu'en dur dans le code.
Pipeline vocal complet avec un LLM
Le TTS prend tout son sens combine a un LLM : l'utilisateur parle, on transcrit (Whisper), le LLM repond, et le TTS vocalise. Voici l'orchestration cote serveur.
// voice-pipeline.js — STT (Whisper) -> LLM -> TTS (ElevenLabs)
import OpenAI from 'openai';
import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js';
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const tts = new ElevenLabsClient({ apiKey: process.env.ELEVENLABS_API_KEY });
async function voiceTurn(audioFile) {
// 1. Transcrire l'audio entrant (speech-to-text)
const transcription = await openai.audio.transcriptions.create({
file: audioFile,
model: 'whisper-1',
});
// 2. Generer une reponse textuelle avec le LLM
const completion = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{ role: 'system', content: 'Reponds en une a deux phrases concises.' },
{ role: 'user', content: transcription.text },
],
});
const answer = completion.choices[0].message.content;
// 3. Vocaliser la reponse (text-to-speech)
const audioStream = await tts.textToSpeech.stream('voice-id-ici', {
text: answer,
modelId: 'eleven_turbo_v2_5',
});
return { userSaid: transcription.text, assistantSaid: answer, audioStream };
}
Ajouter un lecteur "Ecouter l'article"
Le cas d'usage le plus immediat pour un site de contenu : offrir une version audio de chaque article. On expose un endpoint qui genere (et met en cache) l'audio d'un texte, puis un petit composant front qui pilote la lecture avec une barre de progression.
// server : endpoint qui renvoie l'audio cache d'un article
import express from 'express';
const app = express();
app.get('/api/article-audio/:slug', async (req, res) => {
const article = await loadArticle(req.params.slug); // votre source de contenu
if (!article) return res.status(404).end();
// getSpeech() reutilise le cache vu plus bas : zero cout si deja genere
const buffer = await getSpeech(article.plainText, process.env.BRAND_VOICE_ID);
res.setHeader('Content-Type', 'audio/mpeg');
res.setHeader('Cache-Control', 'public, max-age=86400'); // cache navigateur 24h
res.send(buffer);
});
// player.ts — composant "Ecouter l'article" avec progression (TypeScript)
export function initArticlePlayer(slug: string, btn: HTMLButtonElement, bar: HTMLElement) {
const audio = new Audio(`/api/article-audio/${slug}`);
btn.addEventListener('click', () => {
if (audio.paused) {
audio.play();
btn.textContent = 'Pause';
} else {
audio.pause();
btn.textContent = 'Ecouter l\'article';
}
});
// Mettre a jour la barre de progression au fil de la lecture
audio.addEventListener('timeupdate', () => {
const pct = (audio.currentTime / audio.duration) * 100;
bar.style.inlineSize = `${pct}%`;
});
audio.addEventListener('ended', () => { btn.textContent = 'Reecouter'; });
}
Surlignage synchronise pour l'accessibilite
Pour une lecture vraiment accessible (karaoke / read-along), on synchronise le surlignage du texte avec l'audio. L'API d'ElevenLabs peut renvoyer des timestamps par caractere : on les exploite pour surligner le mot en cours de lecture.
// server : recuperer l'audio AVEC les timestamps de caracteres
const result = await client.textToSpeech.convertWithTimestamps('voice-id-ici', {
text: article.plainText,
modelId: 'eleven_multilingual_v2',
});
// result.alignment.characters / .characterStartTimesSeconds
res.json({
audioBase64: result.audioBase64,
chars: result.alignment.characters,
starts: result.alignment.characterStartTimesSeconds, // debut de chaque char (s)
});
// front : surligner le caractere lu en suivant audio.currentTime
function attachReadAlong(audio, starts) {
let idx = 0;
audio.addEventListener('timeupdate', () => {
const t = audio.currentTime;
// Avancer le curseur jusqu'au caractere correspondant au temps courant
while (idx < starts.length && starts[idx] <= t) idx++;
highlightUpTo(idx); // applique une classe .lu sur les caracteres deja lus
});
}
Couts, quotas et alternatives
ElevenLabs facture au caractere genere, avec des quotas mensuels selon le plan. Un long contenu se chiffre vite : mettez en cache l'audio des textes statiques.
// Cache d'audio pour les textes recurrents (evite de regenerer)
import { createHash } from 'node:crypto';
import { existsSync } from 'node:fs';
import { readFile, writeFile } from 'node:fs/promises';
async function getSpeech(text, voiceId) {
// Cle de cache = hash du texte + voix
const key = createHash('sha256').update(text + voiceId).digest('hex');
const path = `./cache/${key}.mp3`;
if (existsSync(path)) {
return readFile(path); // deja genere : zero cout API
}
const stream = await client.textToSpeech.convert(voiceId, { text });
const chunks = [];
for await (const c of stream) chunks.push(c);
const buffer = Buffer.concat(chunks);
await writeFile(path, buffer); // mise en cache pour la prochaine fois
return buffer;
}
| Solution | Qualite | Modele |
|---|---|---|
| ElevenLabs | Excellente, expressive | Payant a l'usage |
| OpenAI TTS | Tres bonne | Payant, integre au SDK |
| Web Speech API | Basique, gratuite | Navigateur, hors ligne |
| Coqui / Piper | Variable | Open source, self-hosted |
Ethique et clonage de voix
Le clonage de voix est techniquement accessible, mais juridiquement et ethiquement sensible. Cloner la voix de quelqu'un sans son consentement explicite est illegal dans de nombreuses juridictions et viole les conditions d'usage des plateformes.
- Consentement ecrit explicite avant tout clonage de voix
- Privilegier les voix de synthese fournies ou sa propre voix
- Mentionner que l'audio est genere par IA quand c'est trompeur autrement
- Ne jamais imiter une personne pour la desinformation ou la fraude
- Respecter les watermarks audio anti-deepfake des plateformes
Erreurs frequentes a l'integration
La premiere erreur, couteuse au sens propre, est de regenerer l'audio a chaque requete. Pour un contenu statique (un article, une notification type, un message d'accueil), le texte ne change pas : son audio doit etre genere une seule fois puis servi depuis un cache ou un CDN. Sans cela, vous payez le TTS au caractere a chaque visiteur, pour un resultat strictement identique. Le pattern de cache par hash vu plus haut est la parade.
Deuxieme piege technique cote navigateur : l'autoplay bloque. Les navigateurs interdisent la lecture audio automatique sans interaction utilisateur prealable. Un audio.play() declenche au chargement de la page echoue silencieusement. Liez toujours le demarrage de la lecture a un clic explicite (le bouton « Ecouter l'article »), et gerez la promesse renvoyee par play() pour detecter un rejet.
Troisieme source de frustration : negliger la nettoyage du texte avant synthese. Du HTML, des emojis ou des URL brutes envoyes au TTS produisent une lecture parasitee (« inferieur p superieur »...). Extrayez le texte propre, developpez les abreviations ambigues et supprimez le balisage avant l'appel. Enfin, choisissez le bon outputFormat selon le canal : un format leger pour le streaming web, une qualite superieure pour un livre audio telecharge — envoyer du sans-perte sur mobile gaspille de la bande passante sans gain percu.
Quand un texte long doit etre decoupe en plusieurs requetes, attention a la coherence de voix entre les fragments. Si vous generez chaque paragraphe avec des voiceSettings legerement differents (ou en laissant le modele varier), l'auditeur percoit des ruptures de ton genantes. Figez les memes parametres de voix sur tout un document, et coupez le texte sur des frontieres naturelles (fin de phrase, fin de paragraphe) plutot qu'a un nombre de caracteres arbitraire qui tronquerait une intonation en plein milieu.
Cote serveur, surveillez enfin les limites de concurrence de votre plan. Generer en masse l'audio de centaines d'articles d'un coup peut saturer le quota et renvoyer des erreurs 429. Une file de jobs avec un nombre de workers plafonne, plus un backoff sur les erreurs de debit, evite de perdre des generations et lisse la charge — exactement comme pour n'importe quelle API LLM facturee a l'usage.
Pensez aussi a l'invalidation du cache : si vous mettez en cache l'audio d'un article et que son texte est corrige, l'ancien audio reste servi tant que la cle de cache (le hash du texte) n'a pas change. En indexant le cache sur le hash du contenu, une modification du texte genere automatiquement une nouvelle cle, donc une nouvelle synthese — sans intervention manuelle. C'est le meme principe que le cache-busting des assets statiques, applique a la voix : aucune purge manuelle, aucune incoherence entre le texte affiche et l'audio lu, et la regeneration ne se declenche que pour les contenus reellement modifies.
Conclusion
Le Text-to-Speech IA a atteint un niveau de naturel qui le rend exploitable en production : narration de contenu, assistants vocaux, accessibilite, e-learning. ElevenLabs offre une API simple, multilingue et expressive, avec un streaming faible latence indispensable aux usages conversationnels.
Combine a Whisper et a un LLM, le TTS complete le pipeline vocal complet. Pensez a mettre en cache les textes statiques pour maitriser les couts factures au caractere, choisissez le modele selon le compromis qualite/latence, et respectez scrupuleusement l'ethique autour du clonage de voix. La voix devient une interface a part entiere de vos applications.
- TTS = generer de la voix naturelle a partir de texte
- stability et similarity_boost modulent l'expressivite
- Streaming + modeles turbo pour la faible latence
- Cacher l'audio des textes recurrents pour reduire les couts
- Consentement obligatoire pour tout clonage de voix