MENU

HTTPステータスコード百人一首の読み上げスクリプト

HTTPステータスコード百人一首の自動音声読み上げスクリプトを作る
 

2人でも、HTTPステータスコード百人一首で遊びたい!

 「HTTPステータスコード百人一首」は、HTTPのステータスの説明文を読み上げて、取り札をとる素敵なカードゲームです。2名以下でも遊べるように、読み上げツールを自作します。

この記事でわかること
  • HTTPステータスコード百人一首の遊び方
  • 音声合成(読み上げ)の実現方法
  • 実装時に困ったことと、回避方法
目次

HTTPステータスコード百人一首とは?

取り札と読み札

 揚げピーナッツさんが作成した新しいカードゲームです。

 お馴染みの HTTPステータスコードで、百人一首ができるというオシャレな作品です。

 本来の推奨人数は「札の読み手:1名、札の取り手:2〜4名」ですが、「札の読み手:0名」で遊べるように読み上げツールを作りました。

音声読み上げツール【完成品】

 

できました!

HTTPステータスコードの読み上げ



速度:
遊び方
  1. 取り札を表向きに並べます。
  2. お好みの設定を選んで、「読み上げ」ボタンをクリック!
  3. 読み上げられた札を、探して取ります。
  4. 表示されているダイアログの「OK」ボタンをクリックすると、読み上げた札の正答が表示されます。
  5. もう一度、「OK」ボタンを押すと、次の札を読み上げます。
  6. 3〜5の手順を、最後の札まで繰り返します。

※ キャンセルを押した場合、途中から再開はできません。お気をつけください。
※ ChromeまたはSafariでお使いください。

 

自主トレにもお使いください。

スクリプトの作成ついて

ソースコード

<div id="message">
  <p id="tool-title">HTTPステータスコードの読み上げ</p>
  <input type="checkbox" id="status-select" name="status-select" checked="checked">
  <label for="status-select">ステータスを読み上げる</label><br>
  <input type="checkbox" id="code-select" name="code-select" checked="checked">
  <label for="code-select">コードを読み上げる</label><br>
  速度:
  <select id="speed-select">
    <option value="0.5">ゆっくり</option>
    <option value="0.8" selected="selected">ふつう</option>
    <option value="1.2">はやい</option>
  </select><br>
  <button id="speak-btn">読み上げ</button>
</div>
<script>
  const speedSelect = document.querySelector('#speed-select')
  const statusSelect = document.querySelector('#status-select')
  const codeSelect = document.querySelector('#code-select')
  const speakBtn    = document.querySelector('#speak-btn')
  
  speakBtn.addEventListener('click', function() {
  
	let card = [{"code":"504","status":"Gateway Time out","reading":"ゲイトウェイ タイム アウト ","description":"ゲートウェイが時間内にレスポンスを返す事ができませんでした。"},{"code":"503","status":"Service Unavailable","reading":"サービス アナヴェイラブル ","description":"リクエストを受け付ける準備ができていません。"},{"code":"502","status":"Bad Gateway","reading":"バッド ゲイトウェイ ","description":"リクエストの処理に必要なレスポンスを受け取るゲートウェイが、無効なレスポンスを受け取りました。"},{"code":"501","status":"Not Implemented","reading":"ノット イムプリメンティド ","description":"サーバーが対応していないメソッドをリクエストしました。"},{"code":"500","status":"Internal Server Error","reading":"インターナル サーバー エラー","description":"サーバーで異常が発生して正しく返答できませんでした。"},{"code":"451","status":"Unavailable For Legal Reasons","reading":"アナヴェイラブル フォー リーガル リーズンズ ","description":"リクエストされたリソースは、政府の検閲によってレスポンスを拒否しました。"},{"code":"429","status":"Too Many Requests","reading":"トゥー メニー リクエスツ","description":"一定の時間内に大量のリクエストを行ったため、サーバーが受付を拒否しました。"},{"code":"425","status":"Too Early","reading":"トゥー アーリー","description":"リプレイ攻撃される可能性があるため、リクエストを受け付けませんでした。"},{"code":"418","status":"I'm a teapot","reading":"アイム ア ティーポット ","description":"ティーポットにコーヒーをいれさせようとして、拒否された場合に返します。これはジョークのコードです。"},{"code":"416","status":"Range Not Satisfiable","reading":"レンジ ノット サティスフィアブル ","description":"実際のリソースサイズと異なるサイズのリクエストをしました。"},{"code":"414","status":"URI Too Long","reading":"ユー-アール-アイ トゥー ロング ","description":"リクエストしたURLが長過ぎて、サーバーは受け付ける事ができませんでした。"},{"code":"413","status":"Payload Too Large","reading":"ペイロード トゥー ラージ ","description":"リクエストの内容が大きく、サーバーで定めている上限を超えました。"},{"code":"412","status":"Precondition Failed","reading":"プリコンディション フェイルド ","description":"リクエストヘッダーに指定された前提条件が適合しませんでした。"},{"code":"411","status":"Length Required","reading":"レングス リクヮイアード ","description":"リクエストにContent-Lengthヘッダーがありません。"},{"code":"410","status":"Gone","reading":"ゴーン ","description":"リクエストされたリソースは永久に消滅しました。"},{"code":"409","status":"Conflict","reading":"コンフリクト ","description":"リクエストが現在のリソースと競合しているため処理が完了できません。"},{"code":"408","status":"Request Time out","reading":"リクエスト タイム アウト","description":"リクエストが時間内に完了しませんでした。"},{"code":"407","status":"Proxy Authentication Required","reading":"プロキシ オーセンティケイション リクヮイアード ","description":"プロキシの認証が必要です。"},{"code":"406","status":"Not Acceptable","reading":"ノット アクセプタブル ","description":"クライアントが受理できないデータがレスポンスに含まれています。"},{"code":"405","status":"Method Not Allowed","reading":"メソッド ノット アロウド","description":"リクエストされたメソッドは許可されていません。"},{"code":"404","status":"Not Found","reading":"ノット ファウンド ","description":"リクエストされたリソースが見つかりませんでした。存在しないページを表示しようとした時などに返されます。"},{"code":"403","status":"Forbidden","reading":"フォービドゥン","description":"リクエストされたリソースへのアクセスが禁止されています。アクセス権がないページを表示しようとした時などに返されます。"},{"code":"401","status":"Unauthorized","reading":"アンオーサライズド ","description":"クライアントに有効な認証資格がありません。"},{"code":"400","status":"Bad Request","reading":"バッド リクエスト","description":"クライアントのエラーによりリクエストが処理できません。リクエストの構文が正しくない場合などに返されます。"},{"code":"308","status":"Permanent Redirect","reading":"パーマネント リダイレクト","description":"リクエストされたURLが変更されています。新しいURLを返します。これは301と同じ意味を持ちますが、HTTPメソッドを変更してはいけない点が異なります。"},{"code":"307","status":"Temporary Redirect","reading":"テンポラリ リダイレクト","description":"リクエストされたURLが一時的に存在しないため、別のURLに転送します。これは302と同じ意味を持ちますが、HTTPメソッドを変更してはいけない点が異なります。"},{"code":"304","status":"Not Modified","reading":"ノット モディファイド ","description":"リクエストされたリソースは更新されていません。"},{"code":"303","status":"See Other","reading":"シー アザー","description":"リクエストに対するレスポンスが他のURLに存在します。"},{"code":"302","status":"Found","reading":"ファウンド ","description":"リクエストされたURLが一時的に存在しないため、別のURLに転送します。"},{"code":"301","status":"Moved Permanently","reading":"ムーヴド パーマネントリー","description":"リクエストされたURLが変更されています。新しいURLを返します。"},{"code":"300","status":"Multiple Choice","reading":"マルチプル チョイス","description":"リクエストに対して複数のレスポンスがあります。"},{"code":"206","status":"Partial Content","reading":"パーシャル コンテント ","description":"リクエストは成功し、リクエストのRangeヘッダーで要求された範囲のコンテンツを返しました。"},{"code":"205","status":"Reset Content","reading":"リセット コンテント","description":"リクエストは成功しましたが、クライアントにコンテンツをリセットするように要求しています。フォームの送信後に画面をリセットする場合などに返されます。"},{"code":"204","status":"No Content","reading":"ノー コンテント ","description":"リクエストは成功しましたが、返すコンテンツがありません。"},{"code":"203","status":"Non-Authoritative Information","reading":"ノン オーソリテイティブ  インフォメーション","description":"リクエストの内容がオリジナルと異なり、ローカルやプロキシ等の情報なので信頼できません。"},{"code":"202","status":"Accepted","reading":"アクセプティッド","description":"リクエストは受理されましたが、まだ実行されていません。"},{"code":"201","status":"Created","reading":"クリエイテッド","description":"リクエストは成功し、新たなリソースのURLが作成されました。"},{"code":"200","status":"OK","reading":"オーケイ ","description":"リクエストは成功しました。"},{"code":"101","status":"Switching Protocol","reading":"スイッチング プロトコル","description":"リクエストを理解できているが、正しい処理を行うため、プロトコルの切り替えを要求しています。"},{"code":"100","status":"Continue","reading":"コンティニュー","description":"リクエストの最初の部分を受け取り、まだ拒否されていません。継続してリクエストすることが可能です。"}]
  
  card = shuffle(card)
 
  let talk = new SpeechSynthesisUtterance()
  talk.rate = speedSelect.value
  talk.lang = "ja-JP"
  talk.pitch = 0.8;
  let msg = "OKを押すと、次の札を読み上げます"

  for(let i in card) {
    talk.text = card[i].description
    if( statusSelect.checked )
      talk.text += card[i].reading
    if( codeSelect.checked )
      talk.text += "。" + card[i].code
      
    speechSynthesis.speak(talk)

    if(! confirm( "OKを押すと、この札の情報を表示します" ) )
    	break
     
    speechSynthesis.cancel()

    if( i == card.length -1 ) 
      msg = "お疲れ様でした!"
    if( ! confirm(card[i].code + ":" + card[i].status + "\n\n" + msg) )
    	break
  }
  speechSynthesis.cancel()
})
const shuffle = (array) => {
  for(i = array.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1))
      const tmp = array[i]
      array[i] = array[j]
      array[j] = tmp
  }
  return array
}
</script>
 

中身はとてもシンプルなJavaScriptです。

Web Speech API

 今回は、Web Speech API をJavaScriptから実行し、読み上げを実現しました。

 Web Speech APIを選んだ理由は以下の通りです。

  1. シンプル
  2. ブラウザだけで動き、動作環境が多い
  3. 無償で実現できる
  4. 追加ライブラリすら要らない
 

高品質な読み上げとは、言えないかもしれません。
しかし、ブラウザだけで動く手軽さは魅力です。

コーディング中に困ったこと

 コーディング中に困ったこと・回避方法を、メモとして書き残しておきます。

  1. ループ2周目から、読み上げられなかった。
    →ループの中で、speechSynthesis.cancel()を呼んだらうまくいった。
  2. 札の読み上げ前に効果音を入れたかった。しかし、読み上げとタイミングが合わない。
    →これは諦めた。
  3. 日本語APIは、英単語の読み上げが不自然だった。URIをユリと読んでしまう。
    →カタカナ英語のデータを作り、これを読み上げることにした。

参考サイト

 コーディングの際に、参考にさせていただいたサイトは以下の通りです。

まとめ

 HTTPステータスコード百人一首は、揚げピーナッツさんのサイトに各種ストアへのリンクがあります。素敵なゲームですので、是非お買い求めください。

 またツールおよびコードの公開について、揚げピーナッツさんにご快諾いただきました。
 誠にありがとうございます。

 最後まで読んでいただき、ありがとうございました。
 みなさまの暮らしがより良くなりますように。

お役に立てたら幸いです
目次
閉じる