A QR Code is a 2-dimensional bar code. They're generally used to encode a URL so someone can just scan the code and visit a site. QR codes can encode all sorts of data beyond just URLs, I used a QR code to pay for my lunch today. In this article, I'll walk you generating and reading QR codes in Node.js using 2 separate libraries.

Generating a QR Code in Node.js

The qrcode npm module is the easiest way to get started generating QR codes in Node.js. First, install qrcode:

npm install qrcode@10.x

The qrcode library has a toDataURL() function that resolves to a base 64 png encoding of a QR code. Below is an example of what the output looks like in the Node.js shell:

$ node -v
v8.9.4
$ node
> const qrcode = require('qrcode')
undefined
> let str
undefined
> const promise = qrcode.toDataURL('foo').then(res => { str = res.substr(0, 25) })
undefined
> str
'data:image/png;base64,iVB'

If you just write this string to a png file, it won't work. But what you can do is write it to an HTML file as the src attribute of an image, and most browsers will be able to render it for you:

const fs = require('fs');
const qrcode = require('qrcode');

run().catch(error => console.error(error.stack));

async function run() {
  const res = await qrcode.toDataURL('http://asyncawait.net');

  fs.writeFileSync('./qr.html', `<img src="${res}">`);
  console.log('Wrote to ./qr.html');
}

Here's the QR code in Chrome:

You can then get a QR code reader app for your phone, like this one for Android, and read the URL from the QR code using your phone's camera.

Reading QR Codes from Node.js

For the purposes of this article, I'll use the qrcode-reader npm module. The qrcode-reader library also relies on the excellent jimp image manipulation library, so you will need to install both.

npm install jimp@0.5.4 qrcode-reader@1.x

Here's a photo of the QR code from the first section that I took with my phone. The angle is a little off and there's some glare, so this is a good test for how the qrcode-reader library behaves under imperfect circumstances.

Below is a minimal example of using qrcode-reader and jimp to read the URL from qr_photo.png above.

const QRReader = require('qrcode-reader');
const fs = require('fs');
const jimp = require('jimp');

run().catch(error => console.error(error.stack));

async function run() {
  const img = await jimp.read(fs.readFileSync('./qr_photo.png'));

  const qr = new QRReader();

  // qrcode-reader's API doesn't support promises, so wrap it
  const value = await new Promise((resolve, reject) => {
    qr.callback = (err, v) => err != null ? reject(err) : resolve(v);
    qr.decode(img.bitmap);
  });

  // { result: 'http://asyncawait.net',
  //   points:
  //     [ FinderPattern {
  //         x: 68.5,
  //         y: 252,
  //         count: 10,
  // ...
  console.log(value);

  // http://asyncawait.net
  console.log(value.result);
}

If you want to get fancy, you can use jimp's scan() function to draw a rectangle on the feature points as shown below.

for (const point of value.points) {
  img.scan(Math.floor(point.x) - 5, Math.floor(point.y) - 5, 10, 10, function(x, y, idx) {
    // Modify the RGBA of all pixels in a 10px by 10px square around the 'FinderPattern'
    this.bitmap.data[idx] = 255; // Set red to 255
    this.bitmap.data[idx + 1] = 0; // Set blue to 0
    this.bitmap.data[idx + 2] = 0; // Set green to 0
    this.bitmap.data[idx + 3] = 255; // Set alpha to 255
  });
}

await img.writeAsync('./qr_photo_annotated.png');

Now, the qr_photo_annotated.png file will look like what you see below.

Moving On

QR codes are a tragically underused tool. You may think they're just for URLs, but they're great for any quick one-off transaction that would otherwise require typing: payments, wifi passwords, and assigning computer-readable identifiers to physical objects. If nothing else, they're a fun application for tinkering with computer vision.

Found a typo or error? Open up a pull request! This post is available as markdown on Github
comments powered by Disqus