jsQR: reading QR codes from raw pixels in the browser or in Node
A pure javascript QR code reading library. This library takes in raw images and will locate, extract and parse any QR code found within.
At a glance
- What is it?
- A TypeScript library that takes RGBA pixel data and returns the decoded string plus corner coordinates. It ships one function, no platform specific code, and a test suite made of hundreds of real photographs.
- Who is it for?
- jsQR earns its place when you already have pixels and want text back, because it handles the hard part of the problem, locating the finder patterns and reading a rotated or perspective warped symbol, and leaves pixel acquisition to you. Pick it over a heavier scanner when you want one function with no camera code of your own.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 144 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One exported function that takes pixels, not a file
The whole API is one method. The README describes it as taking three arguments that represent the image data you wish to decode, plus an optional options object, which reads like this:
const code = jsQR(imageData, width, height, options?);
if (code) {
console.log("Found QR code", code);
}There is no scanner class, no `detect` then `decode` split, and no image format handling. The library is written in TypeScript and ships type definitions at `dist/index.d.ts`, so the return shape is discoverable in an editor rather than by reading the source.
That narrow surface is a deliberate choice, and the README states it plainly: jsQR is designed to be a completely standalone library that by design does not include any platform specific code. The consequence is that the same build scans a frontend webcam stream, a user uploaded image, or a backend Node process, provided the caller has already produced `ImageData`.
The first argument has a specific type and shape. It is a `Uint8ClampedArray` of RGBA pixel values in the form `[r0, g0, b0, a0, r1, g1, b1, a1, ...]`, so its length should be `4 * width * height`. The README notes this is the same form as the browser `ImageData` interface, and that node modules for reading images return it in the same way.
Installing through npm or a script tag
The npm route is the documented one, and the README notes the package works in a Node.js program or with a module bundler such as Webpack or Browserify.
npm install jsqr --saveThe package is published as `jsqr`, all lowercase, while the project and its import name are `jsQR`. That distinction trips people up on a case sensitive filesystem or when reading a stack trace, so it is worth internalising before debugging anything.
For a frontend build that does not want a bundler, the repository ships a prebuilt file. The README points at `dist/jsQR.js` and includes it directly:
<script src="jsQR.js"></script>
<script>
jsQR(...);
</script>The `package.json` sets `main` to `./dist/jsQR.js` and `types` to `./dist/index.d.ts`, so the same artifact serves module resolution and type resolution. There is a live demo hosted at cozmo.github.io/jsQR, which is also where the webcam scanning example lives.
What the return value gives you beyond the string
A successful decode returns `data` as the string form of the QR contents, and `binaryData` as a `Uint8ClampedArray` of the raw bytes. It also returns `version` and `chunks`, which matter when you are working with a symbol that carries structured data rather than a URL.
The field that gets used most is `location`, an object of `{x, y}` points. The README lists the corners as `topRightCorner`, `topLeftCorner`, `bottomRightCorner` and `bottomLeftCorner`, the finder patterns as `topRightFinderPattern`, `topLeftFinderPattern` and `bottomLeftFinderPattern`, and notes that there may also be a `bottomRightAlignmentPattern` if one exists and can be located.
Those points are what let you draw a box over the code in a camera view or crop it before a second read. The alignment pattern is conditional, which is the honest part of the API: on a version 1 symbol there is no alignment pattern, and on a damaged one it may exist but fail to locate, so code that assumes it will be there will break.
A failed decode returns a falsy value rather than throwing, which is why the example guards with `if (code)`. There is no error object describing why the read failed.
Inversion attempts and the half speed you pay for by default
The single option the README documents is `inversionAttempts`, and its default is worth arguing with. It takes four values: `attemptBoth`, which is the default, `dontInvert`, `onlyInvert`, or `invertFirst`.
What it controls is whether the library tries an inverted copy of the image, which is how you find a QR code printed with white modules on a black background instead of the conventional black on white. The README is direct about the cost: `attemptBoth` is the default for backwards compatibility but causes a roughly 50% performance hit, and `dontInvert` will probably become the default in future versions.
That is an unusually candid default and it has a practical consequence. If your use case is ordinary printed or displayed codes, setting `dontInvert` roughly halves the work and costs you the inverted symbol. If your users scan codes off a dark screen or a light terminal, the inversion path is the feature that saves you, and paying for `attemptBoth` is the right call.
The webcam path is documented, but deliberately left to you
The README has a dedicated section on webcams and its content is mostly a boundary. If you want to scan from a webcam you need to extract `ImageData` from the video stream yourself and pass that to jsQR.
For what to do next, it names two starting points: the jsQR demo contains a barebones implementation of webcam scanning that can be used as a starting point and customised, and for more advanced questions it points at the `getUserMedia` documentation and the webRTC samples repository, describing them as comprehensive resources for consuming a webcam stream.
So the library handles decode and location, and leaves frame acquisition, downscaling and frame rate to the caller. That is the honest division of labour for a library that refuses platform specific code, and it also means you own the performance decisions that matter most on a phone, where scanning every video frame of a high resolution stream is the expensive part rather than the decode itself.
Hundreds of real images in the test suite, with a pass and fail report
The repository does something few small libraries do: it treats decoding quality as a measured quantity. Beyond unit tests, the README says the suite contains several hundred images in the `tests/end-to-end/` folder, and not all of them can be read.
The reasoning is unusually honest for a computer vision project. In general, changes should hope to increase the number of images that read, however due to the nature of computer vision some changes may cause images that pass to start to fail and vice versa. When the expected outcomes need regenerating, the script is:
npm run-script generate-test-dataThe results land in `tests/end-to-end/report.json`, and the README describes them as something to evaluate in the context of a pull request, to determine whether a change improves or harms the overall ability of the library to read QR codes. That report is the most useful artifact in the repository for anyone deciding whether jsQR reads the images they actually have.
The rest of the workflow is ordinary TypeScript. Tests run with:
npm testand a production build with `npm run-script build`, which runs webpack behind a `prebuild` that clears `dist` first. Linting is `tslint --project .`, configured by `tslint.json`, with `tsconfig.json` and `webpack.config.js` at the top level.
Maturity signals, and what the repository does not tell you
The project is typed as TypeScript, released under Apache-2.0, hosted at cozmo.github.io/jsQR, and its last push was on 2026-05-15. The repository has no GitHub releases, so there is no published version history to read, which is a real gap when you are deciding whether a change in behaviour is a regression.
The dependency list in `package.json` is worth a second look before adopting it. The development dependencies pin tooling from an earlier era: TypeScript at `^2.5.2`, webpack at `^3.10.0`, jest at `^23.1.0` and `tslint` at `^5.7.0`. That affects contributors cloning and building the source, not consumers installing the published bundle, but it does tell you the build tooling has not been modernised. The test data generator also pulls in `upng-js`, which is how the fixture images are produced.
What the repository does not cover is performance. There are no throughput figures in the README and no benchmark scripts in the tree, so the only quantitative claim available is the inversion cost, which the README states as roughly 50%. Anyone with a latency budget should measure on their own images.
Editorial conclusion
jsQR earns its place when you already have pixels and want text back, because it handles the hard part of the problem, locating the finder patterns and reading a rotated or perspective warped symbol, and leaves pixel acquisition to you. Pick it over a heavier scanner when you want one function with no camera code of your own. Before adopting, look at `tests/end-to-end/report.json` in the repository, which shows which real images the library reads and which it does not, and expect to tune the `inversionAttempts` default, since the shipped `attemptBoth` behaviour costs roughly half the decode speed for a symbol style most applications never see.
Frequently asked questions
How do I use jsQR to scan a QR code from a webcam stream in the browser?
Extract `ImageData` from the video stream yourself and pass it to jsQR along with the width and height. The library deliberately contains no platform specific code, so the README points at its own demo for a barebones webcam implementation and at the `getUserMedia` docs and webRTC samples for frame handling.
Why does jsQR not find a QR code that is white on black?
That symbol style needs the inversion path, which is controlled by the `inversionAttempts` option. The default value is `attemptBoth`, which tries both orientations and costs roughly 50% in performance; try `dontInvert` if you only expect conventional black-on-white codes.
What input format does jsQR expect for an image?
A `Uint8ClampedArray` of RGBA pixel values in the form `[r0, g0, b0, a0, r1, g1, b1, a1, ...]`, whose length should be `4 * width * height`. That is the same shape as the browser `ImageData` interface, and node image decoding modules return it in the same form.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/cozmo-jsqr)