当サイトでは実際に購入した商品のみをレビューし、アフェリエイトリンク付きでご紹介しています
HTTPステータスコード百人一首の読み上げスクリプト
2人でも、HTTPステータスコード百人一首で遊びたい!
「HTTPステータスコード百人一首」は、HTTPのステータスの説明文を読み上げて、取り札をとる素敵なカードゲームです。2名以下でも遊べるように、読み上げツールを自作します。
- HTTPステータスコード百人一首の遊び方
- 音声合成(読み上げ)の実現方法
- 実装時に困ったことと、回避方法
HTTPステータスコード百人一首とは?
揚げピーナッツさんが作成した新しいカードゲームです。
お馴染みの HTTPステータスコードで、百人一首ができるというオシャレな作品です。
本来の推奨人数は「札の読み手:1名、札の取り手:2〜4名」ですが、「札の読み手:0名」で遊べるように読み上げツールを作りました。
音声読み上げツール【完成品】
できました!
HTTPステータスコードの読み上げ
速度:
- 取り札を表向きに並べます。
- お好みの設定を選んで、「読み上げ」ボタンをクリック!
- 読み上げられた札を、探して取ります。
- 表示されているダイアログの「OK」ボタンをクリックすると、読み上げた札の正答が表示されます。
- もう一度、「OK」ボタンを押すと、次の札を読み上げます。
- 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を選んだ理由は以下の通りです。
- シンプル
- ブラウザだけで動き、動作環境が多い
- 無償で実現できる
- 追加ライブラリすら要らない
高品質な読み上げとは、言えないかもしれません。
しかし、ブラウザだけで動く手軽さは魅力です。
コーディング中に困ったこと
コーディング中に困ったこと・回避方法を、メモとして書き残しておきます。
- ループ2周目から、読み上げられなかった。
→ループの中で、speechSynthesis.cancel()を呼んだらうまくいった。 - 札の読み上げ前に効果音を入れたかった。しかし、読み上げとタイミングが合わない。
→これは諦めた。 - 日本語APIは、英単語の読み上げが不自然だった。URIをユリと読んでしまう。
→カタカナ英語のデータを作り、これを読み上げることにした。
参考サイト
コーディングの際に、参考にさせていただいたサイトは以下の通りです。
まとめ
HTTPステータスコード百人一首は、揚げピーナッツさんのサイトに各種ストアへのリンクがあります。素敵なゲームですので、是非お買い求めください。
またツールおよびコードの公開について、揚げピーナッツさんにご快諾いただきました。
誠にありがとうございます。
お買い上げありがとうございます!
— 揚げピーナッツ (@agepeanuts) April 28, 2021
読み札の文言を含めて公開して頂いて問題ありません、是非お願いします!
公開したあとURLを教えていただけるとありがたいです 🙇
最後まで読んでいただき、ありがとうございました。
みなさまの暮らしがより良くなりますように。