Signatur
Beschreibung
SpeechSynthesisVoice beschreibt eine einzelne Stimme, die der Browser oder das Betriebssystem für die Sprachsynthese (Text-to-Speech) anbietet. Eigene Instanzen kann man nicht erzeugen, denn es gibt keinen öffentlichen Konstruktor. Die Objekte bekommt man ausschließlich über speechSynthesis.getVoices(). Danach weist man eine Stimme der Eigenschaft voice eines SpeechSynthesisUtterance zu.
Jede Stimme hat nur schreibgeschützte Eigenschaften:
name(string): lesbarer Name, z. B."Google Deutsch"oder"Anna".lang(string): BCP-47-Sprachcode, z. B."de-DE".voiceURI(string): eindeutige Kennung der Stimme für den jeweiligen Sprachdienst.localService(boolean):true, wenn die Synthese lokal läuft,falsebei einem entfernten Dienst (Cloud).default(boolean):true, wenn das die Standardstimme der aktuellen Sprache bzw. des Systems ist.
Das Interface braucht man immer dann, wenn eine bestimmte Stimme gewählt werden soll, etwa eine deutsche Stimme für Vorlesefunktionen, eine Offline-Stimme für den Datenschutz oder eine Stimmauswahl, die der Nutzer selbst trifft. Ohne ausdrückliche Zuweisung nimmt der Browser eine Standardstimme, die zur Eigenschaft lang der Äußerung passt.
Rückgabewert
speechSynthesis.getVoices() zurückgibt.Beispiele
Verfügbare Stimmen zuverlässig laden
// getVoices() liefert beim ersten Aufruf oft ein leeres Array,
// deshalb auf das Event voiceschanged warten
const loadVoices = () => new Promise((resolve) => {
const voices = speechSynthesis.getVoices();
if (voices.length) return resolve(voices);
speechSynthesis.addEventListener('voiceschanged', () => {
resolve(speechSynthesis.getVoices());
}, { once: true });
});
const voices = await loadVoices();
voices.forEach((v) => {
console.log(`${v.name} (${v.lang})${v.default ? ' – Standard' : ''}${v.localService ? ' [lokal]' : ' [remote]'}`);
});
Deutsche Stimme auswählen und Text vorlesen
const speakGerman = (text) => {
const voices = speechSynthesis.getVoices();
// lokale deutsche Stimme bevorzugen, sonst irgendeine deutsche
const voice =
voices.find((v) => v.lang.startsWith('de') && v.localService) ??
voices.find((v) => v.lang.startsWith('de'));
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'de-DE';
if (voice) utterance.voice = voice;
speechSynthesis.speak(utterance);
return voice?.name ?? 'Standardstimme';
};
console.log(speakGerman('Willkommen auf programmierung.net!'));
Stimmauswahl als Dropdown mit gespeicherter Präferenz
const select = document.querySelector('#voice-select');
const fillVoices = () => {
const voices = speechSynthesis.getVoices();
const saved = localStorage.getItem('voiceURI');
select.replaceChildren(...voices.map((v) => {
const opt = new Option(`${v.name} (${v.lang})`, v.voiceURI);
opt.selected = v.voiceURI === saved || (!saved && v.default);
return opt;
}));
};
fillVoices();
speechSynthesis.onvoiceschanged = fillVoices;
select.addEventListener('change', () => {
localStorage.setItem('voiceURI', select.value);
const voice = speechSynthesis.getVoices().find((v) => v.voiceURI === select.value);
const u = new SpeechSynthesisUtterance('Das ist die ausgewählte Stimme.');
u.voice = voice;
speechSynthesis.speak(u);
});
// Wichtig · Fallstricke
- Stimmen werden asynchron geladen: Vor allem in Chrome gibt
getVoices()anfangs ein leeres Array zurück. Auf das Eventvoiceschangedwarten, das Safari allerdings nicht in jeder Version zuverlässig auslöst. Als Rückfall hilft ein erneuter Aufruf nach kurzer Verzögerung. - Welche Stimmen es gibt, hängt von der Plattform ab: Namen, Anzahl und
voiceURIunterscheiden sich je nach Betriebssystem, Browser und installierten Sprachpaketen. Stimmnamen deshalb nie fest im Code verdrahten, sondern nachlangfiltern und einen Rückfall vorsehen. - Remote-Stimmen und Datenschutz: Bei
localService === falsekann der Text an einen Cloud-Dienst geschickt werden, z. B. bei den Google-Stimmen in Chrome. Für sensible Inhalte lokale Stimmen bevorzugen. Remote-Stimmen brauchen außerdem eine Netzwerkverbindung. - Fingerprinting: Die Liste der Stimmen verrät etwas über das System. Manche Browser schränken sie deshalb ein, etwa Firefox mit
privacy.resistFingerprinting. - Format von
lang: Auf manchen Android-Versionen kommt"de_DE"mit Unterstrich statt Bindestrich. Beim Vergleich darum am bestenstartsWith('de')verwenden. - Stimmobjekte nicht dauerhaft speichern. Nur die
voiceURIablegen und die Stimme später wieder ausgetVoices()heraussuchen.