← ishizakahiroshi.com
設計判断 2026-09-24

API キーなしの音声入力を Web Speech API で作る。Chrome 拡張(MV3)とデスクトップ版(Rust)を共通コアから分解した

作業台の上でマイクの部品が分解されて浮かび、藍色の光の線でブラウザの窓とデスクトップアプリの窓につながっているヘッダー画像

回数制限のない音声入力 vtype は、9 月 19 日に空のリポジトリを作り、24 日にデスクトップ版を npm に出しました。認識は Chrome に最初から入っている Web Speech API を借りているので、API キーもアカウントも要りません。元は自作ダッシュボード many-ai-cli の入力欄にあったマイクボタンで、その認識部分を共通コアとして切り出し、Chrome 拡張とデスクトップ版の 2 つの出口を付けました。その中身を、コード付きで分解した記事です。

Qiita で読む(技術版)→ note で読む(前回: 紹介と Store 申請)→ vtype リポジトリ →
共通コア vtype-core、Chrome 拡張、デスクトップ版の 3 つの柱をまとめた要約図

認識は Chrome 内蔵の Web Speech API を借りる。書いたのは、その手前と後ろです。

共通コア 1 つと、出口が 2 つ

many-ai-cli の音声入力から「認識」の部分だけを切り出して、vtype-core という部品にしました。tsconfig から DOM を外してあり、画面には 1 ピクセルも描きません。Chrome 拡張、デスクトップ版、many-ai-cli 自身の 3 つが、同じ認識のコードを使います。

共通にした 4 つ(認識エンジン、セッション管理、文言、見た目の決まり)と、使う側 3 つの出口を示した全体図

入口(声)と認識は同じで、文字をどこへどう入れるかだけが違います。

拡張は offscreen document、デスクトップ版は画面の外の Chrome

拡張では、認識をページの中ではなく、拡張自身の offscreen document で回します。サイトごとにマイクの許可を聞かれないようにするためで、許可はインストール直後の 1 回だけです。

content script、background、offscreen document の 3 つの間でメッセージが行き来し、結果が録音を始めたフレームへ返る流れの図

content script は音声の API に一度も触りません。

デスクトップ版は、最初は拡張とつなぐ作りでしたが、同じ日に作り直しました。今は Rust の常駐本体が 127.0.0.1 で認識ページを配り、専用プロフィールの Chrome を画面の外で開いて、そこで認識した確定の文字を前面のアプリへ打ち込みます。認識ページは、拡張の offscreen のコードをそのまま使っています。

ショートカットから常駐本体、画面外の Chrome の認識ページ、OS ごとの打ち込み、前面のアプリまでの流れと、127.0.0.1 の守りをまとめた図

常駐本体と Chrome のやり取りは PC の外に出ません。外へ出るのは、Chrome が送る声だけです。

上限を持たないのは vtype の側の話です。Google 側に上限があるかどうかは公式の文書に書かれておらず、根拠は Chromium の開発者の発言だけです。記事ではその出典と、割り引いて読むべき点も書いています。

記事に書いたこと

Qiita 版には、止めるたびに認識のインスタンスを作り直す理由、2 つのエンジンにマイクを同時に持たせない排他、無音 3 回で終わるセッションとサイクルの管理、React の欄にネイティブの setter で入れる方法と IME の変換待ち、サイトのボタンを自動でよけるマイクの置き方、127.0.0.1 のサーバーの守り方、OS ごとの打ち込み方までを、実際のコードの抜粋と一緒に書いています。

vtype の紹介と、デスクトップ版を Microsoft Store に出すまでの話は 前回の note 記事 に、機能の一覧とスクリーンショットは 作品ページ にまとめてあります。