Programming

Free QR code generator: static codes that never expire

Make free QR codes for links, Wi-Fi, email, phone numbers and texts. Static codes that never expire, with PNG and SVG downloads and code for your own site.

Make a QR code for a website, a Wi-Fi network, an email, a phone number or a text message, then download it as a PNG image or an SVG file. The codes are static, so they work for ever: no account, no free trial that runs out, and no tracking. Everything happens in your browser, so nothing you type is sent anywhere.

The calculator needs JavaScript switched on in your browser.

How to use your QR code

  1. Choose what the code should hold and fill in the boxes. The code updates as you type.
  2. Test it: point your phone’s camera at the code on screen and check that it opens the right thing.
  3. Download it. Use the PNG for documents, emails and social media, and the SVG for printing, because it stays sharp at any size.

Printing tips. Keep the white border around the code; scanners need it to find the edges. Make the printed code big enough for the distance people will scan it from: as a rough guide, at least 2 cm wide for a business card or flyer, and around a tenth of the scanning distance for posters. Test the printed copy with two or three different phones before you print a large batch.

Static and dynamic QR codes: why some codes stop working

Some QR code websites make “dynamic” codes. The code holds a short web address that belongs to the QR company, which then sends people on to your link. If your account or free trial ends, that forwarding address can stop working, and every printed copy of the code goes with it.

The codes made here are static. The web address, Wi-Fi details or text are stored in the pattern itself, so the code keeps working for as long as the thing it points to exists. The trade-off is that a static code cannot be changed after printing and cannot count scans. If you need to change where a printed code goes later, point it at a page on your own website that you control.

Wi-Fi QR codes for guests

A Wi-Fi code lets visitors join your network by pointing their phone’s camera at it, without typing the password. Most phones made in the last few years can do this from the camera app. Print it and put it somewhere guests can see it, but remember that anyone who can see the code can join the network, so use a guest network if your router has one.

The code uses the standard Wi-Fi format that phone cameras recognise: WIFI:T:WPA;S:network name;P:password;;. Special characters in the name or password (backslash, semicolon, comma, colon and double quotes) are escaped for you.

Error correction: which level to choose

QR codes contain extra data so they still scan when part of the code is dirty, scratched or covered. There are four levels:

Level Can recover Use it for
Low (L) About 7% of the code Screens and clean prints, when you want the smallest, simplest code
Medium (M) About 15% Most uses; the default here
Quartile (Q) About 25% Printed codes that may get worn
High (H) About 30% Codes outdoors, on packaging, or with a logo placed over the middle

Higher levels make the pattern denser, so the same text needs more, smaller squares. For a long web address, a lower level keeps the code easier to scan.

Colours

Dark squares on a light background scan best. You can use any colours with strong contrast, but light squares on a dark background are not read by every phone, so the generator warns you if you swap them. Avoid placing the code on a busy photo.

For developers: QR codes in JavaScript

This page uses the free, open-source qrcode-generator library by Kazuhiko Arase (MIT licence). It has no other dependencies and works in any browser. The smallest working example, loading the library from the jsDelivr CDN, is:

<div id="qr"></div>
<script src="https://cdn.jsdelivr.net/npm/qrcode-generator@2.0.4/dist/qrcode.js"></script>
<script>
  qrcode.stringToBytes = qrcode.stringToBytesFuncs["UTF-8"]; // accents, £ and emoji
  var qr = qrcode(0, "M");       // 0 = choose the size automatically; M = medium error correction
  qr.addData("https://braindumps.co.uk/");
  qr.make();
  document.getElementById("qr").innerHTML = qr.createSvgTag({ cellSize: 4, margin: 4 });
</script>

To let people save the code as a PNG, draw it on a canvas and use canvas.toDataURL("image/png") or canvas.toBlob(). The full generator on this page, with the Wi-Fi, email and download options, is at the end of this page for you to copy.

Questions people ask

Do QR codes expire?

Static QR codes, like the ones made here, never expire. A code only stops working if it is damaged, or if the website it points to goes away. Dynamic codes from subscription services can stop working when the subscription ends.

Can I use these QR codes for my business?

Yes. The codes are free to use for anything, including printed menus, flyers, packaging and signs. “QR Code” is a registered trademark of DENSO WAVE, which invented the format, but using QR codes is free.

How much can a QR code hold?

The largest QR code holds up to 2,953 bytes at the lowest error correction. That is 2,953 plain English characters; accented letters take two bytes each and emoji four. In practice, keep it much shorter: a short web address makes a simpler code that scans faster.

Can I see how many people scanned my code?

Not with a static code, because nothing passes through a third party. To count visits, add a tracking parameter to your web address (for example ?utm_source=poster) and check your website’s analytics.

More free tools are in the Tools section, including the journey cost calculator and the temperature converter.

Sources

qrcode-generator (Kazuhiko Arase, MIT licence); DENSO WAVE: QR code error correction; DENSO WAVE: QR code versions and capacity; ZXing: barcode contents, including the Wi-Fi format.

Use this QR code generator on your own site

You are welcome to use this generator on your own website. Copy the code below into a Custom HTML block in WordPress, or into any web page. It loads the free qrcode-generator library from the jsDelivr CDN and needs nothing else. We only ask that you keep the link to Braindumps at the bottom.

<!-- Braindumps QR code generator. Static codes that never expire. Everything runs in your browser; nothing is sent anywhere. Loads the qrcode-generator library by Kazuhiko Arase (MIT licence) from the jsDelivr CDN. -->
<div id="bd-qr">
<style>
#bd-qr{--bd-red:#d63333;--bd-line:rgba(127,127,127,.35);--bd-soft:rgba(127,127,127,.08);font:inherit;color:inherit;max-width:760px;margin:1.5em 0}
#bd-qr *{box-sizing:border-box}
#bd-qr [hidden]{display:none!important}
#bd-qr .tabs{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 12px}
#bd-qr .tabs button{padding:7px 13px;border:1px solid var(--bd-line);border-radius:999px;background:transparent;color:inherit;font:inherit;font-size:.92em;cursor:pointer}
#bd-qr .tabs button[aria-selected="true"]{border-color:var(--bd-red);background:var(--bd-soft);font-weight:700}
#bd-qr .grid{display:grid;grid-template-columns:minmax(0,1fr) 260px;gap:18px;align-items:start}
@media (max-width:640px){#bd-qr .grid{grid-template-columns:1fr}}
#bd-qr label{display:block;font-size:.92em;margin-bottom:10px}
#bd-qr input[type=text],#bd-qr input[type=url],#bd-qr input[type=email],#bd-qr input[type=tel],#bd-qr input[type=password],#bd-qr textarea,#bd-qr select{width:100%;padding:8px 10px;margin-top:4px;border:1px solid var(--bd-line);border-radius:6px;font:inherit;background:transparent;color:inherit}
#bd-qr select option{color:#111}
#bd-qr .check{display:flex;align-items:center;gap:8px}
#bd-qr .check input{margin:0}
#bd-qr .row2{display:grid;grid-template-columns:1fr 1fr;gap:10px 14px}
#bd-qr .colours{display:flex;gap:16px;align-items:center;flex-wrap:wrap}
#bd-qr input[type=color]{width:46px;height:34px;padding:2px;border:1px solid var(--bd-line);border-radius:6px;background:transparent;vertical-align:middle;margin-left:6px}
#bd-qr details{border:1px solid var(--bd-line);border-radius:8px;padding:8px 12px;margin:4px 0 0}
#bd-qr summary{cursor:pointer;font-weight:700}
#bd-qr .out{border:2px solid var(--bd-red);border-radius:10px;padding:12px;background:var(--bd-soft);text-align:center}
#bd-qr .qr{background:#fff;border-radius:6px;line-height:0;margin:0 auto;max-width:236px}
#bd-qr .qr svg{width:100%;height:auto;display:block}
#bd-qr .info{font-size:.82em;opacity:.8;margin:8px 0}
#bd-qr .btns{display:flex;gap:8px;justify-content:center;flex-wrap:wrap;margin:0}
#bd-qr .btns button{padding:8px 12px;border:1px solid var(--bd-red);border-radius:6px;background:var(--bd-red);color:#fff;font:inherit;font-size:.9em;cursor:pointer}
#bd-qr .btns button.alt{background:transparent;color:inherit}
#bd-qr .warn{color:var(--bd-red);font-weight:700;font-size:.88em;margin:8px 0 0}
#bd-qr .note{font-size:.82em;opacity:.75;margin:10px 0 0}
</style>

<div class="tabs" role="tablist" aria-label="What the QR code holds">
<button type="button" role="tab" data-type="url" aria-selected="true">Website link</button>
<button type="button" role="tab" data-type="text" aria-selected="false">Text</button>
<button type="button" role="tab" data-type="wifi" aria-selected="false">Wi-Fi</button>
<button type="button" role="tab" data-type="email" aria-selected="false">Email</button>
<button type="button" role="tab" data-type="tel" aria-selected="false">Phone</button>
<button type="button" role="tab" data-type="sms" aria-selected="false">Text message</button>
</div>

<div class="grid">
<div>
<div class="panel" data-type="url">
<label>Web address<input id="bd-qr-url" type="url" value="https://braindumps.co.uk/" autocomplete="off" spellcheck="false"></label>
</div>
<div class="panel" data-type="text" hidden>
<label>Text<textarea id="bd-qr-text" rows="4">Hello from Braindumps</textarea></label>
</div>
<div class="panel" data-type="wifi" hidden>
<label>Network name (SSID)<input id="bd-qr-ssid" type="text" autocomplete="off" spellcheck="false"></label>
<label>Password<input id="bd-qr-pass" type="text" autocomplete="off" spellcheck="false"></label>
<div class="row2">
<label>Security<select id="bd-qr-auth"><option value="WPA">WPA, WPA2 or WPA3</option><option value="WEP">WEP (old routers)</option><option value="nopass">None (open network)</option></select></label>
<label class="check"><input id="bd-qr-hidden" type="checkbox"> Hidden network</label>
</div>
</div>
<div class="panel" data-type="email" hidden>
<label>Email address<input id="bd-qr-to" type="email" autocomplete="off"></label>
<label>Subject (optional)<input id="bd-qr-subject" type="text"></label>
<label>Message (optional)<textarea id="bd-qr-body" rows="3"></textarea></label>
</div>
<div class="panel" data-type="tel" hidden>
<label>Phone number<input id="bd-qr-tel" type="tel" placeholder="+44 7700 900123" autocomplete="off"></label>
</div>
<div class="panel" data-type="sms" hidden>
<label>Phone number<input id="bd-qr-smsto" type="tel" placeholder="+44 7700 900123" autocomplete="off"></label>
<label>Message<textarea id="bd-qr-msg" rows="3"></textarea></label>
</div>
<details>
<summary>Size, colours and error correction</summary>
<div class="row2">
<label>Download size<select id="bd-qr-size"><option value="256">256 pixels</option><option value="512" selected>512 pixels</option><option value="1024">1024 pixels</option><option value="2048">2048 pixels (print)</option></select></label>
<label>Error correction<select id="bd-qr-ecl"><option value="L">Low (7%), smallest code</option><option value="M" selected>Medium (15%)</option><option value="Q">Quartile (25%)</option><option value="H">High (30%), for print</option></select></label>
</div>
<p class="colours"><label class="check">Squares<input id="bd-qr-fg" type="color" value="#000000"></label><label class="check">Background<input id="bd-qr-bg" type="color" value="#ffffff"></label></p>
</details>
</div>
<div class="out" aria-live="polite">
<div class="qr" id="bd-qr-img" role="img" aria-label="QR code"></div>
<p class="info" id="bd-qr-info"></p>
<p class="btns"><button type="button" id="bd-qr-png">Download PNG</button><button type="button" class="alt" id="bd-qr-svg">Download SVG</button></p>
<p class="warn" id="bd-qr-warn" hidden></p>
</div>
</div>
<p class="note">This makes a static QR code: the information is stored in the code itself, so it works for ever, with no account, no tracking and no expiry date. Your text never leaves your browser.</p>

<script src="https://cdn.jsdelivr.net/npm/qrcode-generator@2.0.4/dist/qrcode.js"></script>
<p class="bd-credit" style="font-size:.85em;opacity:.75;margin:10px 0 0">QR code generator by <a href="https://braindumps.co.uk/qr-code-generator-including-code-to-use-on-your-website/">Braindumps.co.uk</a></p>
<script>
(function () {
  "use strict";
  qrcode.stringToBytes = qrcode.stringToBytesFuncs["UTF-8"];

  /* BEGIN CALC */
  function wifiEscape(s) { return String(s).replace(/([\\;,:"])/g, "\\$1"); }
  function digits(s) { return String(s).replace(/[^0-9+]/g, ""); }
  function payload(type, v) {
    switch (type) {
      case "url":
        var u = String(v.url || "").trim();
        if (u && !/^[a-z][a-z0-9+.-]*:/i.test(u)) { u = "https://" + u; }
        return u;
      case "text": return String(v.text || "");
      case "wifi":
        if (!v.ssid) { return ""; }
        return "WIFI:T:" + v.auth + ";S:" + wifiEscape(v.ssid) + ";" + (v.auth === "nopass" ? "" : "P:" + wifiEscape(v.pass || "") + ";") + (v.hidden ? "H:true;" : "") + ";";
      case "email":
        var to = String(v.to || "").trim();
        if (!to) { return ""; }
        var q = [];
        if (v.subject) { q.push("subject=" + encodeURIComponent(v.subject)); }
        if (v.body) { q.push("body=" + encodeURIComponent(v.body)); }
        return "mailto:" + to + (q.length ? "?" + q.join("&") : "");
      case "tel": return digits(v.tel) ? "tel:" + digits(v.tel) : "";
      case "sms": return digits(v.tel) ? "SMSTO:" + digits(v.tel) + ":" + String(v.msg || "") : "";
    }
    return "";
  }
  function luminance(hex) {
    var n = parseInt(hex.slice(1), 16);
    var ch = [(n >> 16) & 255, (n >> 8) & 255, n & 255].map(function (x) { x /= 255; return x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4); });
    return 0.2126 * ch[0] + 0.7152 * ch[1] + 0.0722 * ch[2];
  }
  function contrast(fg, bg) { var a = luminance(fg), b = luminance(bg); return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05); }
  /* END CALC */

  var root = document.getElementById("bd-qr");
  if (!root) { return; }
  function el(id) { return document.getElementById(id); }
  var type = "url";
  var MARGIN = 4; // the quiet zone the QR standard asks for
  var current = null;

  function values() {
    return {
      url: el("bd-qr-url").value, text: el("bd-qr-text").value,
      ssid: el("bd-qr-ssid").value, pass: el("bd-qr-pass").value, auth: el("bd-qr-auth").value, hidden: el("bd-qr-hidden").checked,
      to: el("bd-qr-to").value, subject: el("bd-qr-subject").value, body: el("bd-qr-body").value,
      tel: type === "sms" ? el("bd-qr-smsto").value : el("bd-qr-tel").value, msg: el("bd-qr-msg").value
    };
  }
  function svgFor(qr, fg, bg) {
    var n = qr.getModuleCount(), size = n + MARGIN * 2, d = "";
    for (var r = 0; r < n; r++) { for (var c = 0; c < n; c++) { if (qr.isDark(r, c)) { d += "M" + (c + MARGIN) + "," + (r + MARGIN) + "h1v1h-1z"; } } }
    return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ' + size + " " + size + '" shape-rendering="crispEdges"><rect width="100%" height="100%" fill="' + bg + '"/><path d="' + d + '" fill="' + fg + '"/></svg>';
  }
  function warn(msg) { el("bd-qr-warn").hidden = !msg; el("bd-qr-warn").textContent = msg || ""; }

  function update() {
    var text = payload(type, values());
    var fg = el("bd-qr-fg").value, bg = el("bd-qr-bg").value;
    current = null;
    if (!text) {
      el("bd-qr-img").innerHTML = ""; el("bd-qr-info").textContent = "Fill in the boxes to make your QR code."; warn(""); return;
    }
    var qr = qrcode(0, el("bd-qr-ecl").value);
    try { qr.addData(text, "Byte"); qr.make(); }
    catch (e) { el("bd-qr-img").innerHTML = ""; el("bd-qr-info").textContent = ""; warn("That is too much for one QR code. Shorten it, or choose a lower error correction."); return; }
    var n = qr.getModuleCount();
    current = { qr: qr, fg: fg, bg: bg, svg: svgFor(qr, fg, bg) };
    el("bd-qr-img").innerHTML = current.svg;
    el("bd-qr-img").style.background = bg;
    el("bd-qr-info").textContent = "Version " + ((n - 17) / 4) + ", " + n + " × " + n + " squares, " + text.length + " characters.";
    var cr = contrast(fg, bg);
    if (luminance(fg) > luminance(bg)) { warn("Light squares on a dark background do not scan with every phone. Swap the colours."); }
    else if (cr < 4) { warn("The colours are too close together to scan reliably. Use darker squares or a lighter background."); }
    else { warn(""); }
  }

  function download(blob, name) {
    var a = document.createElement("a");
    a.href = URL.createObjectURL(blob); a.download = name;
    document.body.appendChild(a); a.click(); a.remove();
    setTimeout(function () { URL.revokeObjectURL(a.href); }, 4000);
  }
  el("bd-qr-png").addEventListener("click", function () {
    if (!current) { return; }
    var px = parseInt(el("bd-qr-size").value, 10);
    var n = current.qr.getModuleCount() + MARGIN * 2;
    var cell = Math.max(1, Math.floor(px / n));
    var size = Math.max(px, cell * n);
    var off = Math.floor((size - cell * n) / 2); // spare pixels go into the white border, so squares stay sharp
    var cv = document.createElement("canvas"); cv.width = cv.height = size;
    var cx = cv.getContext("2d");
    cx.fillStyle = current.bg; cx.fillRect(0, 0, size, size);
    cx.fillStyle = current.fg;
    var m = current.qr.getModuleCount();
    for (var r = 0; r < m; r++) { for (var c = 0; c < m; c++) { if (current.qr.isDark(r, c)) { cx.fillRect(off + (c + MARGIN) * cell, off + (r + MARGIN) * cell, cell, cell); } } }
    cv.toBlob(function (b) { if (b) { download(b, "qr-code.png"); } }, "image/png");
  });
  el("bd-qr-svg").addEventListener("click", function () {
    if (!current) { return; }
    download(new Blob([current.svg], { type: "image/svg+xml" }), "qr-code.svg");
  });

  root.querySelectorAll(".tabs button").forEach(function (b) {
    b.addEventListener("click", function () {
      type = b.getAttribute("data-type");
      root.querySelectorAll(".tabs button").forEach(function (x) { x.setAttribute("aria-selected", x === b ? "true" : "false"); });
      root.querySelectorAll(".panel").forEach(function (p) { p.hidden = p.getAttribute("data-type") !== type; });
      update();
    });
  });
  root.addEventListener("input", update);
  root.addEventListener("change", update);
  update();
})();
</script>
</div>

Get new Braindumps guides by email

One email a week with new guides, the best free 3D prints, LEGO news and tools. Unsubscribe at any time.

No spam. Read our privacy policy.