A pure JavaScript QR code reading library that processes raw images to locate, extract and parse QR codes.
npx @tessl/cli install tessl/npm-jsqr@1.4.0jsQR is a pure JavaScript QR code reading library that processes raw image data to locate, extract and parse QR codes. It operates entirely in JavaScript without external dependencies, making it suitable for both browser and Node.js environments.
npm install jsqr// ES6 import
import jsQR from "jsqr";
// CommonJS require
const jsQR = require("jsqr");For browser usage with script tag:
<script src="jsQR.js"></script>
<script>
// jsQR is available globally
</script>import jsQR from "jsqr";
// Get RGBA image data from canvas, image, or other source
const imageData = canvas.getContext('2d').getImageData(0, 0, width, height);
// Process the image data
const code = jsQR(imageData.data, imageData.width, imageData.height);
if (code) {
console.log("Found QR code:", code.data);
console.log("Location:", code.location);
} else {
console.log("No QR code found");
}Processes raw RGBA image data to find and decode QR codes, returning comprehensive information about the decoded content and location.
/**
* Scans image data for QR codes and returns decoded information
* @param data - RGBA pixel data as Uint8ClampedArray [r0,g0,b0,a0,r1,g1,b1,a1,...]
* @param width - Image width in pixels
* @param height - Image height in pixels
* @param providedOptions - Optional scanning configuration
* @returns Decoded QR code data or null if no valid QR code found
*/
function jsQR(
data: Uint8ClampedArray,
width: number,
height: number,
providedOptions?: Options
): QRCode | null;Parameters:
data - An Uint8ClampedArray of RGBA pixel values in the form [r0, g0, b0, a0, r1, g1, b1, a1, ...]. The length should be 4 * width * height. This matches the format of browser ImageData interface and common Node.js image processing libraries.width - The width of the image in pixelsheight - The height of the image in pixelsprovidedOptions (optional) - Configuration options for scanning behaviorReturns:
QRCode object if a valid QR code is found and successfully decodednull if no QR code is found or decoding failsinterface Options {
/**
* Image inversion strategy for finding QR codes on different backgrounds
* - "attemptBoth": Try both normal and inverted (default, ~50% performance hit)
* - "dontInvert": Only scan normal image
* - "onlyInvert": Only scan inverted image
* - "invertFirst": Try inverted first, then normal if no result
*/
inversionAttempts?: "dontInvert" | "onlyInvert" | "attemptBoth" | "invertFirst";
}
interface QRCode {
/** Raw bytes of the QR code as numeric array */
binaryData: number[];
/** Decoded string content of the QR code */
data: string;
/** Array of decoded data chunks with encoding information */
chunks: Chunks;
/** QR code version number (1-40, indicating size and capacity) */
version: number;
/** Precise pixel coordinates of key QR code features */
location: {
/** Corner coordinates of the decoded QR code area */
topRightCorner: Point;
topLeftCorner: Point;
bottomRightCorner: Point;
bottomLeftCorner: Point;
/** Finder pattern centers (the square detection patterns) */
topRightFinderPattern: Point;
topLeftFinderPattern: Point;
bottomLeftFinderPattern: Point;
/** Alignment pattern center (may not exist for smaller QR codes) */
bottomRightAlignmentPattern?: Point;
};
}
interface Point {
/** X coordinate in pixels */
x: number;
/** Y coordinate in pixels */
y: number;
}
type Chunks = Array<Chunk | ByteChunk | ECIChunk>;
interface Chunk {
/** Data encoding mode used for this chunk */
type: Mode;
/** Decoded text content */
text: string;
}
interface ByteChunk {
/** Binary data encoding mode */
type: Mode.Byte | Mode.Kanji;
/** Raw byte data as numeric array */
bytes: number[];
/** Decoded text content (may be empty string if decoding fails) */
text: string;
}
interface ECIChunk {
/** Extended Channel Interpretation mode */
type: Mode.ECI;
/** ECI assignment number for character encoding */
assignmentNumber: number;
}
enum Mode {
/** Numeric digits 0-9 */
Numeric = "numeric",
/** Alphanumeric characters (0-9, A-Z, space, $, %, *, +, -, ., /, :) */
Alphanumeric = "alphanumeric",
/** 8-bit byte data */
Byte = "byte",
/** Kanji characters (Shift JIS encoding) */
Kanji = "kanji",
/** Extended Channel Interpretation for character encoding */
ECI = "eci"
}import jsQR from "jsqr";
// From canvas element
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
const code = jsQR(imageData.data, imageData.width, imageData.height);
if (code) {
console.log("QR Code found:", code.data);
}import jsQR from "jsqr";
// In a video frame processing loop
function tick() {
if (video.readyState === video.HAVE_ENOUGH_DATA) {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
context.drawImage(video, 0, 0, canvas.width, canvas.height);
const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
const code = jsQR(imageData.data, imageData.width, imageData.height);
if (code) {
console.log("Found QR code:", code.data);
// Access location information
console.log("Top-left corner:", code.location.topLeftCorner);
}
}
requestAnimationFrame(tick);
}import jsQR from "jsqr";
// For better performance, skip inversion attempts if you know your QR codes
// are standard black-on-white
const code = jsQR(imageData.data, width, height, {
inversionAttempts: "dontInvert"
});
// For white-on-dark QR codes, only try inverted
const code2 = jsQR(imageData.data, width, height, {
inversionAttempts: "onlyInvert"
});import jsQR from "jsqr";
const code = jsQR(imageData.data, width, height);
if (code) {
console.log("Decoded text:", code.data);
console.log("Raw bytes:", code.binaryData);
console.log("QR version:", code.version);
// Access encoding chunks
code.chunks.forEach((chunk, index) => {
console.log(`Chunk ${index}:`, chunk.type);
if (chunk.type === "byte" || chunk.type === "kanji") {
console.log("Raw bytes:", chunk.bytes);
console.log("Decoded text:", chunk.text);
} else if (chunk.type === "eci") {
console.log("ECI assignment:", chunk.assignmentNumber);
} else {
console.log("Text:", chunk.text);
}
});
// Draw detection outline on canvas
const corners = [
code.location.topLeftCorner,
code.location.topRightCorner,
code.location.bottomRightCorner,
code.location.bottomLeftCorner
];
context.strokeStyle = "red";
context.beginPath();
corners.forEach((corner, index) => {
if (index === 0) {
context.moveTo(corner.x, corner.y);
} else {
context.lineTo(corner.x, corner.y);
}
});
context.closePath();
context.stroke();
}