以下の内容はhttps://thinkami.hatenablog.com/entry/2025/12/26/203222より取得しました。


Web標準のMediaDevices APIやBarcodeDetector APIを使って、Androidで動くPWA版バーコードリーダーを作ってみた

5年くらい前、Djangoでバーコードを出力する機能も備えたアプリを作りました。
ダンボールに入れた本を管理するDjangoアプリ「danborary」を作った - メモ的な思考的な

当時、バーコードを読むためには物理バーコードリーダーを用意していました。ただ、物理バーコードリーダーはPCと接続する必要があることから、運用の手間を感じるようになりました。

 
時は流れ、ふと「スマホバーコードリーダーを作ればよいのでは」と思い立ちました。

まずはWeb標準でカメラを扱えないかを調べたところ、 MediaDevices APIがありました。
MediaDevices - Web API | MDN

サポート状況を確認したところ、スマホであれば扱えそうでした。
"mediaDevices" | Can I use... Support tables for HTML5, CSS3, etc

次に、バーコードを検知・解析するWeb標準がないかを調べたところ、BarcodeDetector APIがありました。
BarcodeDetector - Web API | MDN

サポート状況を確認したところ、AndroidChromeであれば BarcodeDetector APIは使えそうでした。

 
あとはPWA対応しておけば、Web標準だけでスマホバーコードリーダーが作れそうでした。

ただ、今までPWA、MediaDevices、BarcodeDetector APIを使ったことがないこともありゼロから作るのは大変そうでした。

そこで、

という進め方にしたことから、その時のメモを残します。

 
目次

 

環境

 

PWA版のバーコードリーダーについて

今回のPWA版のバーコードリーダーは、DevContainer上の public ディレクトリに置いたHTML + JavaScriptファイルで構成されています。これをDevContainer上のPythonのHTTPサーバーを使って配信します。

カメラ利用のためには安全なコンテキスト(HTTPS通信)が必要なことから、前回の記事のようにTailscaleを介してHTTPS化します。
DevContainer上でHTMLを配信しているHTTPサーバにHTTPSでアクセスするため、localtunnelやTailscaleを試してみた - メモ的な思考的な

前回の記事の再掲となりますが、経路はこんな感じです。

[DevContainer]
   |
   | (port forward 5180)
   v
[mac]
   |
   | (tailscale serve --https=5190)
   v
[Tailscale tailnet]
   |
   | (WireGuard VPN)
   v
[Android (Tailscaleログイン済み)]

 
あとはOpenSpecに依頼してバーコードリーダーを作ります。

最初は、使いたいAPIや構成を伝えつつ必要最低限のPWAアプリを作りました。
https://github.com/thinkAmi-sandbox/pwa_barcode_reader-example/tree/main/openspec/changes/archive/2025-12-24-add-pwa-barcode-scan

動作確認すると、カメラ画像のプレビューと実際にスキャンしたバーコードで位置がズレていたことから、その対応を行いました。
https://github.com/thinkAmi-sandbox/pwa_barcode_reader-example/tree/main/openspec/changes/archive/2025-12-24-update-scan-region

あとは細かな調整をした感じです。詳しくは後述のリポジトリにある openspec 以下のディレクトリの中身を確認してください。OpenSpecが作ったドキュメントが置いてあります。

 

実装の確認メモ

動くものができたところで実装を確認しました。

自分が詳しくない部分を中心にAIに聞いたり調べたところをメモしておきます。

 

バーコードリーダーの概要

  • ブラウザ標準の BarcodeDetector API を使い、外部ライブラリなしでバーコード/QRを検出する軽量アプリ
  • カメラ映像を video に流し、読み取り枠の範囲だけを canvas に切り出して検出精度と負荷を最適化
  • TypeScript を最小構成でビルドし、静的ファイルとして配信する構成

 

スマホカメラの利用

src/app.tsstartScan 関数にある以下の部分にて、利用するスマホの背面カメラからストリームを得られるようにしています。

stream = await navigator.mediaDevices.getUserMedia({  
    video: {  
       facingMode: { ideal: "environment" },  
       aspectRatio: { ideal: 4 / 3 },  
    },  
    audio: false,  
});

 
getUserMediaの引数は次の通りです。

 
得られたストリームは videoタグのメディアソースとして割り当てます。
HTMLMediaElement: srcObject プロパティ - Web API | MDN

video.srcObject = stream;

 
続いて、メタデータ(解像度、縦横比、再生時間)などを読み込んだときに発生するイベント loadedmetadata にて、カメラ映像の縦横比に合わせてプレビュー枠を更新する関数 updatePreviewAspectを呼んでいます。
HTMLMediaElement: loadedmetadata イベント - Web API | MDN

video.addEventListener("loadedmetadata", updatePreviewAspect, {  
    once: true,  
});

 
準備ができたら、 play でカメラ画像を再生します。
HTMLMediaElement: play() メソッド - Web API | MDN

await video.play();

 
あとは BarcodeDetector オブジェクトを生成します。
BarcodeDetector() - Web API | MDN

今回対象とするのはQRコードとEAN-13なため、formatを指定して誤検知を防ぎます。
バーコード検出 API - Web API | MDN

 

バーコードの解析

scanLoop関数にて解析しています。

videoタグの画像全体を渡すと解析に負荷がかかるため、 drawScanFrame 関数で読み取り枠内の画像をcanvas化しています。

const frame = drawScanFrame();

 
そのcanvasを元に、BarcodeDetector.detectで解析をします。
BarcodeDetector.detect() - Web API | MDN

解析できた場合は、バーコードデータをデコードした文字列を画面に反映します。

const barcodes = await detector.detect(frame);  
if (barcodes.length > 0) {  
    const value = barcodes[0].rawValue || "";  
    if (value && value !== lastValue) {  
       lastValue = value;  
       setResult(value);  
    }  
}

 

PWA版バーコードリーダーのインストール

今回のPWA版バーコードリーダーはスマホへのインストールを可能とします。一方で、必要最低限の実装としたいため、Service Workerによるオフライン操作は不要とします。

そこで、Webアプリマニフェストとして public/manifest.webmanifest だけを用意しました。

{
    "name": "PWA版バーコードスキャナー",
    "short_name": "PWA版バーコードスキャナー",
    "start_url": ".",
    "display": "standalone",
    "background_color": "#f3f0e8",
    "theme_color": "#0f1a1c",
    "icons": [
        {
            "src": "icons/icon-192.svg",
            "sizes": "192x192",
            "type": "image/svg+xml"
        },
        {
            "src": "icons/icon-512.svg",
            "sizes": "512x512",
            "type": "image/svg+xml"
        }
    ]
}

 

動作確認

次の手順で動作準備をします。

  • DevContainer上で public ディレクトリをHTTPサーバーで公開する
    • DevContainer上のターミナルで python -m http.server --directory public 5180
  • Tailscaleの準備をする
    • macAndroid、両方でTailscaleをONにする
  • Tailscale CLIで、リバースプロキシを起動する
    • macのターミナルで tailscale serve --https=5190 5180

Tailscale CLIの画面に表示されたURLをAndroidで開くと、バーコードリーダーが表示されました。読み取り枠にバーコードを入れたところ、値を正しく取得できました。

 

ソースコード

GitHubに上げました。
https://github.com/thinkAmi-sandbox/pwa_barcode_reader-example




以上の内容はhttps://thinkami.hatenablog.com/entry/2025/12/26/203222より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14