speechSynthesisで音声合成を実装する(Web Speech API入門)

speechSynthesisで音声合成を実装する(Web Speech API入門) スクラップ


ブラウザ標準のWeb Speech API(speechSynthesis)を使ったテキスト読み上げの基本的な使い方を、コード例とともに整理しました。

はじめに

ブラウザだけでテキストを音声で読み上げたい場面があり、ライブラリ無しでもspeechSynthesisという標準APIで実現できることを知ったので、使い方を整理しておきます。

外部サービスやライブラリを使わずに、ブラウザ標準機能だけで読み上げが完結するのが大きな魅力です。

speechSynthesisとは

speechSynthesisは、Web Speech APIのうち音声合成(読み上げ)を担う部分です。window.speechSynthesisとしてグローバルに公開されており、SpeechSynthesisUtteranceというオブジェクトに読み上げ内容や設定を詰めて渡すことで、ブラウザのTTS(Text-to-Speech)エンジンが発話してくれます。

なお、音声認識(マイク入力をテキスト化する)の方はSpeechRecognitionという別のAPIで、混同しやすいので注意が必要です。

基本的な使い方

最低限のコードはこれだけです。

const utterance = new SpeechSynthesisUtterance("こんにちは、世界");
speechSynthesis.speak(utterance);

SpeechSynthesisUtteranceに読み上げたいテキストを渡し、speechSynthesis.speak()に渡すだけで再生が始まります。

主なプロパティ

SpeechSynthesisUtteranceには、発話内容を調整するプロパティがいくつか用意されています。

const utterance = new SpeechSynthesisUtterance("こんにちは、世界");

utterance.lang = "ja-JP"; // 言語
utterance.rate = 1.0; // 速度(0.1〜10、既定値1)
utterance.pitch = 1.0; // 音の高さ(0〜2、既定値1)
utterance.volume = 1.0; // 音量(0〜1、既定値1)

speechSynthesis.speak(utterance);

langを明示的に指定しないと、テキストの言語と読み上げ音声が噛み合わずおかしな発音になることがあるため、基本的には指定しておいた方が安全です。

音声(voice)の選択

利用可能な音声はspeechSynthesis.getVoices()で取得できます。OSやブラウザにインストールされている音声合成エンジンによって、選択肢の種類や数は変わります。

function listVoices() {
  const voices = speechSynthesis.getVoices();
  voices.forEach((voice) => {
    console.log(
      `${voice.name} (${voice.lang})${voice.default ? " [default]" : ""}`,
    );
  });
}

listVoices();

注意点として、getVoices()はページ読み込み直後だと空配列を返すことがあります。これは音声エンジンの読み込みが非同期で行われるためで、voiceschangedイベントを使って取得し直す必要があります。

speechSynthesis.addEventListener("voiceschanged", () => {
  console.log(speechSynthesis.getVoices());
});

特定の音声を使いたい場合は、utterance.voiceSpeechSynthesisVoiceオブジェクトを設定します。

const voices = speechSynthesis.getVoices();
const jaVoice = voices.find((v) => v.lang === "ja-JP");

const utterance = new SpeechSynthesisUtterance("こんにちは、世界");
if (jaVoice) {
  utterance.voice = jaVoice;
}

speechSynthesis.speak(utterance);

再生制御(pause / resume / cancel)

読み上げ中の制御も可能です。

speechSynthesis.pause(); // 一時停止
speechSynthesis.resume(); // 再開
speechSynthesis.cancel(); // 読み上げ中のものを含めてすべて停止

speechSynthesis.speakingで発話中かどうか、speechSynthesis.pendingで発話待ちのキューがあるかどうかを確認できます。

speak()は呼び出すたびに内部キューに追加される仕組みのため、同じ内容を続けて呼び出すと意図せず多重に再生されることがあります。前の発話を必ず止めたい場合は、speak()の前にcancel()を呼ぶのが安全です。

function speak(text) {
  speechSynthesis.cancel();
  const utterance = new SpeechSynthesisUtterance(text);
  utterance.lang = "ja-JP";
  speechSynthesis.speak(utterance);
}

イベントハンドリング

SpeechSynthesisUtteranceはいくつかのイベントを発火するため、再生状態に応じた処理を挟めます。

const utterance = new SpeechSynthesisUtterance("こんにちは、世界");
utterance.lang = "ja-JP";

utterance.onstart = () => console.log("読み上げ開始");
utterance.onend = () => console.log("読み上げ終了");
utterance.onerror = (event) => console.error("エラー:", event.error);
utterance.onboundary = (event) => {
  // 単語や文の境界で発火(name: "word" または "sentence")
  console.log(`boundary: ${event.name} at ${event.charIndex}`);
};

speechSynthesis.speak(utterance);

onboundaryを使うと、読み上げ位置に合わせてハイライト表示するような実装もできます。

ブラウザ対応・注意点

  • 主要ブラウザ(Chrome、Edge、Safari、Firefox)はすべて対応していますが、利用可能な音声の種類や品質はOS・ブラウザ依存
  • 多くのブラウザでは、ユーザー操作(クリックなど)を伴わないページ読み込み直後の自動再生がブロックされる
  • タブを閉じる、ページ遷移するとキューに溜まった発話も含めてすべて停止する
  • 長文を一度に渡すと途中で止まる・エラーになるブラウザもあるため、文単位など適度な長さに分割してspeak()を呼ぶのが安全

まとめ

  • speechSynthesisはブラウザ標準のテキスト読み上げAPIで、ライブラリなしで音声合成ができる
  • SpeechSynthesisUtterancelang/rate/pitch/volume/voiceを設定してspeak()で再生する
  • 利用可能な音声はgetVoices()で取得するが、voiceschangedイベントを待つ必要がある場合がある
  • pause/resume/cancelで再生制御ができ、speak()前のcancel()で多重再生を防げる
  • onstart/onend/onerror/onboundaryなどのイベントで読み上げ状態を細かく扱える
  • 自動再生制限や長文での挙動差など、ブラウザ依存の注意点がある