実際に使ってみる
この記事が良かったら
「チップをリクエスト」で著者にチップの受け取り設定をお願いできます
きっかけ 技術記事やドキュメント、下書き中の企画書——読みたいMarkdownファイルは手元にたくさんあるのに、画面に向かって目で追う時間がなかなか取れない。家事をしながら、通勤しながら、あるいは目を休めながらでも中身を頭に入れたい。 そんな動機から生まれたわけではないのが「読み…
実際に使ってみる
この記事が良かったら
「チップをリクエスト」で著者にチップの受け取り設定をお願いできます
技術記事やドキュメント、下書き中の企画書——読みたいMarkdownファイルは手元にたくさんあるのに、画面に向かって目で追う時間がなかなか取れない。家事をしながら、通勤しながら、あるいは目を休めながらでも中身を頭に入れたい。
そんな動機から生まれたわけではないのが「読み上げリーダー(Markdown Voice Reader)」です。Markdownファイルを読み込むと、見た目はそのままきれいに整形して表示しつつ、文章を音声で読み上げてくれる、シンプルなWebアプリです。
KOUSUKEさんの案デス、、
やっていることは大きく分けて2つだけです。
1. Markdownをちゃんと表示する 見出し、リスト、チェックリスト、表、コードブロック、引用——一通りのMarkdown記法に対応しています。ファイルを選ぶか、ドラッグ&ドロップするか、URLを直接指定するだけで読み込めます。
2. 読み上げながら、今どこを読んでいるか教えてくれる 再生を始めると、今読んでいる一文がハイライトされます。文章を目で追わなくても、聞きながら「今ここだな」と把握できるので、ながら聞きから急に画面を見ても迷いません。文中の好きな場所をクリックすれば、そこから読み上げがすぐ始まります。
読み上げエンジンは2種類から選べます。
そのほか、見出しの前に「見出し」と読み上げるかどうか、リンクを「リンクがあります」と案内するか、コードブロックを読み飛ばすか——といった細かい読み方のルールも、全部自分好みに設定できます。地味ですが、実際に「聞く」文書として仕上げるには一番効いてくる部分です。
Markdownをレンダリングした後のHTMLには、<strong>や<a>のようなタグが文の途中に混ざります。「一文ごとに読み上げてハイライトする」ためには、この入り組んだHTML構造をそのままに、文の切れ目だけを正確に見つけてタグで囲む必要がありました。
素直にRange.surroundContents()を使うと、「文の先頭がちょうど<strong>の内側から始まっている」ようなごく普通のケースで、ブラウザの仕様上エラーが出ることが分かりました(該当要素が“部分的に含まれている”とみなされるためです)。最終的にはextractContents()で中身を取り出してから包み直す方式に切り替えて解決しています。地味な話ですが、テキストの見た目を一切崩さずに「聞く」体験を作るには避けて通れない部分でした。
読み上げ中にシークバーをドラッグすると、位置がガクガクして思った場所に飛ばない——というのは、この手のアプリでよくある不満点です。原因は単純で、ドラッグ中に発生するinputイベントのたびに読み上げエンジンへ再生指示を送ってしまい、非同期処理同士が競合していたことでした。
対策として、ドラッグ中は見た目の位置だけを更新し、指を離した瞬間に一度だけ実際のシークを実行するように変更。さらに「再生トークン」という仕組みを入れて、シーク前に発行された古い読み上げ完了イベントが新しい再生を邪魔しないようにしました。
ブラウザ標準の音声合成は、タブがバックグラウンドになると仕様上一時停止されることがあります(特にiOS Safari)。定期的にresume()を呼ぶ延命処理は入れていますが、完全な保証はできません。一方でElevenLabsエンジンは実際の音声ファイルを<audio>要素で再生しているため、Media Session APIとも相性がよく、ロック画面からの操作にも対応できました。「できないことは、できないと正直に書く」——地味ですが大事にした姿勢です。
フロントエンドは依存を最小限に抑えた単一のHTMLファイルです。Markdownのパースにmarked、サニタイズにDOMPurify、コード表示にhighlight.js、数式にKaTeX、図にMermaidを使っています。ElevenLabsのAPIキーをブラウザに一切渡さないよう、呼び出しはCloudflare Workerを1つ挟んでいて、これは全利用者が共有する構成です。GitHub Pagesと組み合わせれば、サーバー管理もほぼゼロで動かせます。
大きな機能を詰め込んだアプリではありません。むしろ「読みたいのに読めない」という、地味だけれど誰もが一度は感じたことのある不便さに、できるだけまっすぐ向き合ったつもりです。手元のMarkdownを、今日から「聞く」文書にしてみませんか。