qr-scanner vs html5-qrcode vs jsqr vs qrcode-reader
Web 環境における QR コード読み込みライブラリの選定とアーキテクチャ比較
qr-scannerhtml5-qrcodejsqrqrcode-reader類似パッケージ:

Web 環境における QR コード読み込みライブラリの選定とアーキテクチャ比較

html5-qrcodejsqrqr-scannerqrcode-reader は、すべて Web ブラウザ上で QR コードをデコードするための JavaScript ライブラリですが、提供される抽象化レベルとアーキテクチャが異なります。html5-qrcode はカメラアクセスから UI 表示までを包括的に扱う高レベルなラッパーであり、jsqr は画像データからコードを抽出する純粋なデコードエンジンに特化しています。qr-scanner は Web Worker を活用した現代的なパフォーマンス重視のライブラリで、qrcode-reader は古くから存在するコールバックベースのライブラリですが、現在はメンテナンスの面で劣ります。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
qr-scanner155,3122,862524 kB118-MIT
html5-qrcode06,1202.63 MB4333年前Apache-2.0
jsqr04,007-965年前Apache-2.0
qrcode-reader0283-179年前Apache-2.0

Web 環境における QR コード読み込みライブラリの選定とアーキテクチャ比較

Web アプリケーションで QR コードを読み込む機能を実装する際、html5-qrcodejsqrqr-scannerqrcode-reader という 4 つの主要な選択肢が存在します。これらはすべて同じ目的を果たしますが、内部の仕組み、抽象化のレベル、そしてメンテナンス状況が大きく異なります。アーキテクトとして、単に「動くもの」を選ぶのではなく、プロジェクトの長期維持性とパフォーマンス特性に合わせて選定する必要があります。

📷 カメラアクセスと抽象化レベル:フルパッケージ vs エンジン単体

ライブラリ選定で最初に直面するのは、「カメラ制御まで含めるか、デコード処理だけを任せるか」という点です。

html5-qrcode は、カメラのパーミッション取得、ストリームの開始、停止、そしてデコードまでをすべて管理する高レベルなライブラリです。UI コンポーネントも付属しているため、実装時間が最短で済みます。

// html5-qrcode: カメラ制御とデコードを一元管理
import { Html5Qrcode } from "html5-qrcode";

const html5QrCode = new Html5Qrcode("reader");
const config = { fps: 10, qrbox: { width: 250, height: 250 } };

html5QrCode.start(
  { facingMode: "environment" },
  config,
  (decodedText) => console.log(decodedText)
);

qr-scanner も同様にカメラ制御を提供しますが、より軽量でモダンな設計になっています。Promise ベースの API を採用しており、async/await との相性が良く、コードがフラットに保てます。

// qr-scanner: モダンな Promise ベースのカメラ制御
import QrScanner from "qr-scanner";

const video = document.getElementById('video');
const qrScanner = new QrScanner(video, result => console.log(result));

await qrScanner.start();
// 停止時は await qrScanner.stop();

jsqr はカメラ制御を行いません。画像データ(ImageData)を受け取り、デコード結果を返す純粋な関数です。カメラ周りは navigator.mediaDevices を自分で実装する必要があります。

// jsqr: デコードエンジン単体。カメラ制御は自前実装
import jsQR from "jsqr";

// video 要素から ImageData を取得する処理は自前で記述
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const code = jsQR(imageData.data, imageData.width, imageData.height);

if (code) {
  console.log(code.data);
}

qrcode-reader もデコードに特化していますが、API が古く、ファイルや画像要素を読み込ませてコールバックを待つ形式です。カメラストリームのリアルタイム処理には向いていません。

// qrcode-reader: 画像読み込みとコールバックベース
import QrcodeReader from "qrcode-reader";

const qrcode = new QrcodeReader();
qrcode.read(imageElement); // imageElement は img タグなど

qrcode.ondecode = (text) => console.log(text);
qrcode.onerror = (err) => console.error(err);

⚡ パフォーマンスと非同期処理:Web Worker とメインスレッド

QR コードのデコード処理は計算コストが高く、メインスレッドで行うと UI がフリッカーする原因になります。

qr-scanner の最大の強みは、デコード処理を Web Worker で実行できる点です。これにより、重い計算がメインスレッドをブロックせず、スクロールやアニメーションが滑らかに保たれます。

// qr-scanner: Web Worker を指定してメインスレッドを保護
import QrScanner from "qr-scanner";

// worker  파일을 명시적으로指定
const qrScanner = new QrScanner(
  video, 
  result => console.log(result),
  { highlightScanRegion: true, highlightCodeOutline: true }
);

html5-qrcode は内部でデコードエンジンを使用していますが、基本的にメインスレッドで動作します。設定でフレームレートを制限することで負荷を調整する必要があります。

// html5-qrcode: fps 制限でパフォーマンスを調整
const config = { 
  fps: 5, // 1 秒あたりのスキャン回数を制限
  qrbox: { width: 250, height: 250 } 
};

jsqrqrcode-reader もメインスレッドで動作します。jsqr は軽量ですが、高解像度の画像を頻繁に処理する場合は、自分で Worker 化を検討する必要があります。

// jsqr: 自前で Worker 化する場合のイメージ
// worker.js 内で jsQR を呼び出す
self.onmessage = (e) => {
  const code = jsQR(e.data.image, e.data.width, e.data.height);
  self.postMessage(code);
};

🔌 API 設計と統合のしやすさ:Promise vs Callback

現代のフロントエンド開発では、非同期処理の扱い方が開発体験(DX)に直結します。

qr-scannerhtml5-qrcode は Promise をサポートしており、async/await でスッキリ書けます。特に qr-scanner は API が一貫しており、エラーハンドリングも try-catch で統一的に処理可能です。

// qr-scanner: 静的メソッドで簡易スキャンも可能
try {
  const result = await QrScanner.scanImage(fileInput.files[0]);
  console.log(result);
} catch (error) {
  console.error("QR code not found");
}

qrcode-reader は典型的なイベントエミッター形式です。複数の非同期処理を直列につなぐ場合、コールバック地獄に陥りやすく、現代のフレームワーク(React, Vue など)との統合にはラッパー処理が必要になります。

// qrcode-reader: コールバックチェーンが必要
googleLoader.load(); // 依存ロード処理などが必要になる場合も
qrcode.read(image);
// ondecode, onerror を個別にハンドリング

jsqr は同期関数です。これは逆に柔軟性が高く、Canvas の描画ループ(requestAnimationFrame)の中に自然に組み込めます。

// jsqr: 描画ループ内で同期呼び出し
function tick() {
  if (video.readyState === video.HAVE_ENOUGH_DATA) {
    const code = jsQR(imageData.data, width, height);
    // 結果を即座に描画に反映可能
  }
  requestAnimationFrame(tick);
}

🛠️ 保守性とエコシステム:メンテナンス状況の確認

ライブラリの選定において、メンテナンス状況はセキュリティとブラウザ互換性に直結する最重要事項です。

  • html5-qrcode: 活発にメンテナンスされており、バグ修正や機能追加が頻繁に行われています。コミュニティも大きく、トラブルシューティングが容易です。
  • qr-scanner: 比較的新しく、モダンなブラウザ機能を前提としているため、コードベースがクリーンです。依存関係も少なく、アップデートリスクが低いです。
  • jsqr: 機能は凍結気味ですが、安定しており「壊れない」ライブラリです。依存ゼロという強みがあり、長期的な保守コストは低いです。
  • qrcode-reader: 更新頻度が低く、実質的にメンテナンスが止まっている可能性があります。新規プロジェクトでの採用はリスクが高く、避けるべきです。

🤝 共通点:ブラウザ標準技術への依存

これら 4 つのライブラリは、特定のブラウザ拡張に依存せず、標準的な Web API をベースにしています。

1. 📹 MediaDevices API の利用

カメラアクセスには、すべて navigator.mediaDevices.getUserMedia を前提としています。

// 全ライブラリの基礎となるカメラアクセス
const stream = await navigator.mediaDevices.getUserMedia({ 
  video: { facingMode: "environment" } 
});

2. 🖼️ Canvas API による画像処理

ビデオフレームを画像データとして取り出す際、すべて CanvasRenderingContext2D を経由します。

// 共通の画像データ抽出パターン
ctx.drawImage(video, 0, 0, width, height);
const imageData = ctx.getImageData(0, 0, width, height);

3. 🌐 ES6 モジュール対応

すべて npm パッケージとして提供されており、import 文でモダンなバンドラ(Vite, Webpack)に取り込めます。

// 共通のインポート形式
import Lib from 'package-name';

📊 比較サマリー

特徴html5-qrcodeqr-scannerjsqrqrcode-reader
抽象化高(カメラ+UI)中(カメラ+デコード)低(デコードのみ)低(画像読み込み)
非同期Promise / CallbackPromise同期関数Callback
Worker非対応対応自前実装必要非対応
依存関係ほぼなしほぼなしなしほぼなし
保守性🟢 活発🟢 活発🟡 安定🔴 低調
推奨度⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

💡 結論:アーキテクトとしての推奨

qr-scanner が、パフォーマンスと開発体験のバランスにおいて最も優れています。Web Worker による非ブロッキング動作と、モダンな Promise API は、現代の Web アプリケーションに不可欠な要素です。

html5-qrcode は、カメラ制御の実装コストを徹底的に下げたい場合や、すぐにプロトタイプを動かしたい場合に最適な選択です。機能の豊富さは圧倒的です。

jsqr は、カメラ制御を自作したい場合や、既存のビデオ処理パイプラインに組み込む場合に適しています。依存関係がないため、長期アーカイブ用のプロジェクトにも向いています。

qrcode-reader は、特別な理由(レガシーコードの維持など)がない限り、新規プロジェクトでは選択すべきではありません。

最終的には、「カメラ制御まで含めるか(qr-scanner / html5-qrcode)」、「デコードのみ必要か(jsqr)」という要件定義の段階で絞り込むと、スムーズな選定が可能です。

選び方: qr-scanner vs html5-qrcode vs jsqr vs qrcode-reader

  • qr-scanner:

    メインスレッドのブロックを避け、高いパフォーマンスを確保したい場合に選択します。Web Worker による非同期処理を標準でサポートしており、モダンな Promise ベースの API を採用しているため、最新のフロントエンドフレームワークとの相性が抜群です。

  • html5-qrcode:

    カメラパーミッションの管理から UI 表示までを一手に任せたい場合に選択します。セットアップが最も簡単で、ドキュメントも充実しているため、スピード重視のプロトタイプや、カスタマイズより確実性を求める本番環境に適しています。

  • jsqr:

    カメラ制御を自身で実装するか、すでに画像データ(Canvas や Video 要素)を入手している場合に選択します。依存関係がなく純粋な JavaScript で書かれているため、バンドルサイズを最小限に抑えたい場合や、独自の UI/UX を完全に制御したいアーキテクチャに向いています。

  • qrcode-reader:

    既存のレガシーシステムを維持する場合を除き、新規プロジェクトでの使用は推奨しません。API がコールバックベースで古く、メンテナンス頻度も他と比べて低いため、現代的な非同期処理(async/await)との統合に苦労する可能性があります。

qr-scanner のREADME

QR Scanner

Javascript QR Code Scanner based on Cosmo Wolfe's javascript port of Google's ZXing library.

In this library, several improvements have been applied over the original port:

  • Web cam scanning support out of the box
  • Uses the browser's native BarcodeDetector if available
  • Lightweight: ~59.3 kB (~16.3 kB gzipped) minified with Google's closure compiler. If the native BarcodeDetector is available, only ~15.3 kB (~5.6 kB gzipped) are loaded.
  • Improved performance and reduced memory footprint.
  • Runs in a WebWorker which keeps the main / UI thread responsive.
  • Can be configured for better performance on colored QR codes.

According to our benchmarking this project's scanner engine's detection rate is about 2-3 times (and up to 8 times) as high as the one of the most popular javascript QR scanner library LazarSoft/jsqrcode. Also the other library oftentimes misreads the content of QR codes, while for this project no misreads occurred in the benchmarking.

The library supports scanning a continuous video stream from a web cam as well as scanning of single images.

The development of this library is sponsored by nimiq, world's first browser based blockchain.

nimiq.com

Demo

See https://nimiq.github.io/qr-scanner/demo/

Installation

To install via npm:

npm install --save qr-scanner

To install via yarn:

yarn add qr-scanner

Or simply copy qr-scanner.min.js and qr-scanner-worker.min.js to your project.

Setup

The QR Scanner consists of two main files. qr-scanner.min.js is the main API file which loads the worker script qr-scanner-worker.min.js via a dynamic import, only if needed. If you are not using a bundler like Rollup or Webpack that handles dynamic imports automatically, you might have to copy qr-scanner-worker.min.js over to your dist, next to qr-scanner.min.js or next to the script into which you're bundling qr-scanner.min.js.

qr-scanner.min.js is an es6 module and can be imported as follows:

import QrScanner from 'path/to/qr-scanner.min.js'; // if using plain es6 import
import QrScanner from 'qr-scanner'; // if installed via package and bundling with a module bundler like webpack or rollup

This requires the importing script to also be an es6 module or a module script tag, e.g.:

<script type="module">
    import QrScanner from 'path/to/qr-scanner.min.js';
    // do something with QrScanner
</script>

If your project is not based on es6 modules you can

import('path/to/qr-scanner.min.js').then((module) => {
    const QrScanner = module.default;
    // do something with QrScanner
});
  • use the UMD build qr-scanner.umd.min.js for direct usage as non-module script
<script src="path/to/qr-scanner.umd.min.js"></script>
<script>
    // do something with QrScanner
</script>
  • bundle qr-scanner.umd.min.js directly with your non-module code with tools like gulp or grunt.
  • bundle the lib with require based bundlers like browserify:
const QrScanner = require('qr-scanner'); // if installed via package
const QrScanner = require('path/to/qr-scanner.umd.min.js'); // if not installed via package
// do something with QrScanner

This library uses ECMAScript 2017 features like async functions. If you need to support old browsers, you can use qr-scanner.legacy.min.js, which is ECMAScript 2015 (ES6) compatible. It's a UMD build and can be used as a replacement for qr-scanner.umd.min.js, see above. Note, that the legacy build is larger as it includes some polyfills and, to support browsers that don't support dynamic imports, inlines the worker script which however would be needed to be loaded in legacy browsers anyway. You will likely not need to use the legacy build though, as general browser support is already very good for the regular build. Especially if you want to scan from the device's camera, camera support by the browser is the stricter restriction.

Usage

Web Cam Scanning

1. Create HTML

Create a <video> element where the web cam video stream should get rendered:

<video></video>

2. Create a QrScanner Instance

// To enforce the use of the new api with detailed scan results, call the constructor with an options object, see below.
const qrScanner = new QrScanner(
    videoElem,
    result => console.log('decoded qr code:', result),
    { /* your options or returnDetailedScanResult: true if you're not specifying any other options */ },
);

// For backwards compatibility, omitting the options object will currently use the old api, returning scan results as
// simple strings. This old api will be removed in the next major release, by which point the options object is then
// also not required anymore to enable the new api.
const qrScanner = new QrScanner(
    videoElem,
    result => console.log('decoded qr code:', result),
    // No options provided. This will use the old api and is deprecated in the current version until next major version.
);

As an optional third parameter an options object can be provided. Supported options are:

OptionDescription
onDecodeErrorHandler to be invoked on decoding errors. The default is QrScanner._onDecodeError.
preferredCameraPreference for the camera to be used. The preference can be either a device id as returned by listCameras or a facing mode specified as 'environment' or 'user'. The default is 'environment'. Note that there is no guarantee that the preference can actually be fulfilled.
maxScansPerSecondThis option can be used to throttle the scans for less battery consumption. The default is 25. If supported by the browser, the scan rate is never higher than the camera's frame rate to avoid unnecessary duplicate scans on the same frame.
calculateScanRegionA method that determines a region to which scanning should be restricted as a performance improvement. This region can optionally also be scaled down before performing the scan as an additional performance improvement. The region is specified as x, y, width and height; the dimensions for the downscaled region as downScaledWidth and downScaledHeight. Note that the aspect ratio between width and height and downScaledWidth and downScaledHeight should remain the same. By default, the scan region is restricted to a centered square of two thirds of the video width or height, whichever is smaller, and scaled down to a 400x400 square.
highlightScanRegionSet this option to true for rendering an outline around the scan region on the video stream. This uses an absolutely positioned div that covers the scan region. This div can either be supplied as option overlay, see below, or automatically created and then accessed via qrScanner.$overlay. It can be freely styled via CSS, e.g. by setting an outline, border, background color, etc. See the demo for examples.
highlightCodeOutlineSet this option to true for rendering an outline around detected QR codes. This uses an absolutely positioned div on which an SVG for rendering the outline will be placed. This div can either be supplied as option overlay, see below, or be accessed via qrScanner.$overlay. The SVG can be freely styled via CSS, e.g. by setting the fill color, stroke color, stroke width, etc. See the demo for examples. For more special needs, you can also use the cornerPoints directly, see below, for rendering an outline or the points yourself.
overlayA custom div that can be supplied for use for highlightScanRegion and highlightCodeOutline. The div should be a sibling of videoElem in the DOM. If this option is supplied, the default styles for highlightCodeOutline are not applied as the expectation is that the element already has some custom style applied to it.
returnDetailedScanResultEnforce reporting detailed scan results, see below.

To use the default value for an option, omit it or supply undefined.

Results passed to the callback depend on whether an options object was provided:

  • If no options object was provided, the result is a string with the read QR code's content. The simple string return type is for backwards compatibility, is now deprecated and will be removed in the future.
  • If an options object was provided the result is an object with properties data which is the read QR code's string content and cornerPoints which are the corner points of the read QR code's outline on the camera stream.

To avoid usage of the deprecated api if you're not supplying any other options, you can supply { returnDetailedScanResult: true } to enable the new api and get the detailed scan result.

3. Start scanning

qrScanner.start();

Call it when you're ready to scan, for example on a button click or directly on page load. It will prompt the user for permission to use a camera. Note: to read from a Web Cam stream, your page must be served via HTTPS.

4. Stop scanning

qrScanner.stop();

If you want, you can stop scanning anytime and resume it by calling start() again.

Single Image Scanning

QrScanner.scanImage(image)
    .then(result => console.log(result))
    .catch(error => console.log(error || 'No QR code found.'));

Supported image sources are: HTMLImageElement, SVGImageElement, HTMLVideoElement, HTMLCanvasElement, ImageBitmap, OffscreenCanvas, File / Blob, Data URIs, URLs pointing to an image (if they are on the same origin or CORS enabled)

As an optional second parameter an options object can be provided. Supported options are:

OptionDescription
scanRegionA region defined by x, y, width and height to which the search for a QR code should be restricted. As a performance improvement this region can be scaled down before performing the scan by providing a downScaledWidth and downScaledHeight. Note that the aspect ratio between width and height and downScaledWidth and downScaledHeight should remain the same. By default, the region spans the whole image and is not scaled down.
qrEngineA manually created QR scanner engine instance to be reused. This improves performance if you're scanning a lot of images. An engine can be manually created via QrScanner.createQrEngine(QrScanner.WORKER_PATH). By default, no engine is reused for single image scanning.
canvasA manually created canvas to be reused. This improves performance if you're scanning a lot of images. A canvas can be manually created via a <canvas> tag in your markup or document.createElement('canvas'). By default, no canvas is reused for single image scanning.
disallowCanvasResizingRequest a provided canvas for reuse to not be resized, irrespective of the source image or source region dimensions. Note that the canvas and source region should have the same aspect ratio to avoid that the image to scan gets distorted which could make detecting QR codes impossible. By default, the canvas size is adapted to the scan region dimensions or down scaled scan region for single image scanning.
alsoTryWithoutScanRegionRequest a second scan on the entire image if a scanRegion was provided and no QR code was found within that region. By default, no second scan is attempted.
returnDetailedScanResultEnforce reporting detailed scan results, see below.

To use the default value for an option, omit it or supply undefined.

Returned results depend on whether an options object was provided:

  • If no options object was provided, the result is a string with the read QR code's content. The simple string return type is for backwards compatibility, is now deprecated and will be removed in the future.
  • If an options object was provided the result is an object with properties data which is the read QR code's string content and cornerPoints which are the corner points of the read QR code's outline on the camera stream.

To avoid usage of the deprecated api if you're not supplying any other options, you can supply { returnDetailedScanResult: true } to enable the new api and get the detailed scan result.

If no QR code could be read, scanImage throws.

Checking for Camera availability

This library provides a utility method for checking whether the device has a camera. This can be useful for determining whether to offer the QR web cam scanning functionality to a user.

QrScanner.hasCamera(); // async

Getting the list of available Cameras

This library provides a utility method for getting a list of the device's cameras, defined via their id and label. This can be useful for letting a user choose a specific camera to use.

You can optionally request the camera's labels. Note that this however requires the user's permission to access the cameras, which he will be asked for if not granted already. If not specifically requested, device labels are determined on a best effort basis, i.e. actual labels are returned if permissions were already granted and fallback labels otherwise. If you want to request camera labels, it's recommendable to call listCameras after a QrScanner instance was successfully started, as by then the user will already have given his permission.

QrScanner.listCameras(); // async; without requesting camera labels
QrScanner.listCameras(true); // async; requesting camera labels, potentially asking the user for permission

Specifying which camera to use

You can change the preferred camera to be used. The preference can be either a device id as returned by listCameras or a facing mode specified as 'environment' or 'user'. Note that there is no guarantee that the preference can actually be fulfilled.

qrScanner.setCamera(facingModeOrDeviceId); // async

Color Inverted Mode

The scanner by default scans for dark QR codes on a bright background. You can change this behavior to scan for bright QR codes on dark background or for both at the same time:

qrScanner.setInversionMode(inversionMode);

Where inversionMode can be original, invert or both. The default for web cam scanning is original and for single image scanning both.

Color Correction

Change the weights for red, green and blue in the grayscale computation to improve contrast for QR codes of a specific color:

qrScanner.setGrayscaleWeights(red, green, blue, useIntegerApproximation = true);

Where red, green and blue should sum up to 256 if useIntegerApproximation === true and 1 otherwise. By default, these values are used.

Flashlight support

On supported browsers, you can check whether the currently used camera has a flash and turn it on or off. Note that hasFlash should be called after the scanner was successfully started to avoid the need to open a temporary camera stream just to query whether it has flash support, potentially asking the user for camera access.

qrScanner.hasFlash(); // check whether the browser and used camera support turning the flash on; async.
qrScanner.isFlashOn(); // check whether the flash is on
qrScanner.turnFlashOn(); // turn the flash on if supported; async
qrScanner.turnFlashOff(); // turn the flash off if supported; async
qrScanner.toggleFlash(); // toggle the flash if supported; async.

Clean Up

You can destroy the QR scanner if you don't need it anymore:

qrScanner.destroy();
qrScanner = null;

This will stop the camera stream and web worker and cleans up event listeners. The QR scanner will be dysfunctional after it has been destroyed.

Build the project

The project is prebuild in qr-scanner.min.js in combination with qr-scanner-worker.min.js. Building yourself is only necessary if you want to change the code in the /src folder. NodeJs is required for building.

Install required build packages:

yarn

Building:

yarn build