QR code generator with HTML, CSS and JavaScript: a lesson
Try the live tool: QR code generator →
QR code → PNG
Runs in your browser; nothing is sent to a server.
What is JavaScript QR Code Generator?
A QR code is a two-dimensional barcode that stores text as a grid of black and white squares ("modules"). The three big squares in the corners help a scanner find and turn the code. The rest of the grid carries the data and its redundancy. A QR code stores only text. The phone reads the text, and if it looks like a URL, it offers to open it. That is why a QR code can point anywhere, safe or harmful.
What is JavaScript QR Code Generator used for?
This lesson joins a library, canvas, and file download in one small project.
- Turning a site link into a QR for print
- Putting text into a QR offline
- Learning to add a ready library to a project
- Downloading a PNG with canvas.toDataURL
What can you do with JavaScript QR Code Generator?
The project is four files: index.html, style.css, script.js and lib/qrcode-generator.js (Kazuhiko Arase, MIT, v1.4.4). The HTML loads the library first, then script.js. When the form is submitted, the code checks the text, builds the module matrix with qrcode(0, 'M') and draws it on a canvas. Size is chosen between 128 and 512 px, and colors come from <input type="color">. The same foreground and background color is refused, because it would not scan. The download button's href becomes canvas.toDataURL('image/png'), so the PNG matches the screen. If the library is a local file, the tool also works offline. For the full QR tool with many types, see /qr.
Is JavaScript QR Code Generator hard to learn?
Encoding a QR from scratch is hard: version choice, blocks, Reed–Solomon error correction and the mask. That is why a ready library is used. Level M tolerates about 15% damage, but it does not fix poor contrast or a cut quiet zone. Very long text needs many modules: if each module gets less than one pixel at 128 px, the code is not drawn and a larger size is asked for. For too much text the library throws "code length overflow", and the code catches it and shows a clear message.
Advantages and limits
Advantage: no build, no server, the text goes nowhere, and it works offline. Limit: a QR code that scans is not a QR code that is safe. Treat an unknown QR like an unchecked link and read the domain before opening.
Basic ideas
The main ideas the code stands on:
- qrcode(0, 'M'): 0 = picks the version itself, M = ~15% error correction
- isDark(row, col): is this module dark?
- Quiet zone: 4 empty modules around the edge
- Download with canvas.toDataURL('image/png')
A short example
Calling the library, drawing on canvas, and setting the PNG link:
1. Turning text into a QR matrix:
// <script src="lib/qrcode-generator.js"></script> loads window.qrcode const qr = qrcode(0, "M"); qr.addData(text, "Byte"); qr.make(); const modules = qr.getModuleCount();
qrcode(0, "M") makes an empty QR object: 0 means the library picks the version (size) itself, M means ~15% error correction.
addData adds the text as UTF-8 bytes, and make() builds the full module matrix.
modules is the number of squares on one side, for example 25 for a short link. Nothing is on screen yet; there is only a grid of dark and light.
2. Module pixel size and the quiet zone:
const QUIET_ZONE = 4;
const cell = Math.floor(size / (modules + QUIET_ZONE * 2));
if (cell < 1) throw new Error("Choose a larger size for this much text.");
const offset = Math.floor((size - cell * modules) / 2);QUIET_ZONE is a blank border of 4 modules around the code. Scanners use this gap to find the code.
cell is how many pixels one square gets: the chosen size (for example 256 px) divided by modules plus the borders.
If cell is below 1, the code does not fit this size. Instead of drawing a clipped QR, the user is told to choose a larger size.
offset puts the code in the middle of the canvas.
3. Drawing the matrix on the canvas:
canvas.width = canvas.height = size;
const ctx = canvas.getContext("2d");
ctx.fillStyle = bg;
ctx.fillRect(0, 0, size, size);
ctx.fillStyle = fg;
for (let r = 0; r < modules; r++)
for (let c = 0; c < modules; c++)
if (qr.isDark(r, c)) ctx.fillRect(offset + c * cell, offset + r * cell, cell, cell);First the whole canvas is filled with the background color (bg); this becomes the light square and the quiet zone.
Two loops walk every row and column. When isDark(r, c) is true, one square in the foreground color (fg) is drawn there.
Result: the QR code you see on screen, with three corner squares and the dots in between.
4. The PNG download link:
downloadLink.href = canvas.toDataURL("image/png");toDataURL turns the picture on the canvas into a PNG file and puts it in the link.
When you click Download PNG, the browser saves that file. It has the same size and colors as the preview, because it comes from the same canvas.
The whole path: text → matrix → canvas → PNG.
Common questions
- Are QR codes free? Yes. The standard is open and the library is MIT licensed.
- Do I need the internet? Not after the page and the local library have loaded once.
- Is the text sent to a server? No. Encoding happens in the browser; no fetch, cookies or analytics.
- If a QR scans, is the site safe? No. A QR is only text; check the domain yourself.
- Where is the full tool? On nibrascode.com/qr.