{"id":"771f990d-0e9b-4d1c-a4b3-180c7143d6a5","name":"QR Code","slug":"qr-code","category":"qr-codes","description":"A scannable QR code as a plain image URL: pass a domain, get a crisp 1024x1024 code on a transparent background. Four parameters and no more — what to encode, the module colour, the background, and an optional logo URL punched into the middle at the error-correction level that survives it.","sample_params":{"bg":"#ffffff","domain":"media0.io","fg":"#0b0c0f"},"spec":{"name":"qr-code","width":256,"height":256,"scale":4,"html":"\u003c!doctype html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n\u003cmeta charset='utf-8'\u003e\n\u003cstyle\u003e\n*{margin:0;padding:0;box-sizing:border-box}\nhtml,body{width:100%;height:100%}\nbody{overflow:hidden;display:flex;align-items:center;justify-content:center}\n/* The canvas is built in DEVICE pixels and scaled back to exactly that\n   many device pixels, so every module edge lands on a pixel boundary;\n   pixelated keeps any residual rounding from softening it. */\n#qr canvas{display:block;image-rendering:pixelated}\n#logo{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);object-fit:contain}\n\u003c/style\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003cdiv id='qr'\u003e\u003c/div\u003e\n{{if .logo}}\u003cimg id='logo' src='{{.logo}}' alt=''\u003e{{end}}\n\u003cscript src='https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js'\u003e\u003c/script\u003e\n\u003cscript\u003e\n// An absent param interpolates as null here, not \"\" — hence every || ''.\nvar RAW = String({{.domain}} || '').trim();\nvar FG = {{.fg}} || '#000000';\nvar BG = {{.bg}} || 'transparent';\n\n// A bare host becomes https://; anything that already carries a scheme\n// (https:, mailto:, tel:, WIFI:) is encoded verbatim.\nvar payload = /^[A-Za-z][A-Za-z0-9+.-]*:/.test(RAW) ? RAW : 'https://' + RAW;\n\n// Only the background is painted: the modules keep the canvas transparent\n// wherever they are light, so bg is the single thing standing behind them.\ndocument.body.style.background = BG;\n\n// qrcodejs asynchronously swaps its canvas for a PNG \u003cimg\u003e; that swap can\n// land after the screenshot, so keep the canvas and skip it.\nQRCode.prototype.makeImage = function () {};\n\nvar side = document.documentElement.clientWidth;\nvar dpr = window.devicePixelRatio || 1;\nvar opts = {text: payload, correctLevel: QRCode.CorrectLevel.H, colorDark: FG, colorLight: 'transparent'};\n\n// Two passes: the first only reveals how many modules the payload needs,\n// so the second can pick a module size that is a whole number of device\n// pixels. +4 leaves a 2-module quiet zone — half of what the spec asks\n// for, because the code is nearly always dropped onto a page that keeps\n// going, and that page is the rest of the quiet zone.\nvar probe = new QRCode(document.createElement('div'), Object.assign({width: 64, height: 64}, opts));\nvar count = probe._oQRCode.getModuleCount();\nvar mod = Math.max(1, Math.floor(side * dpr / (count + 4)));\nvar inner = mod * count;\n\nnew QRCode(document.getElementById('qr'), Object.assign({width: inner, height: inner}, opts));\nvar cv = document.querySelector('#qr canvas');\ncv.style.width = cv.style.height = (inner / dpr) + 'px';\n\nvar logo = document.getElementById('logo');\nif (logo) {\n  // Punch a rounded hole through the modules rather than covering them, so\n  // on a transparent background the logo sits on the consumer's own\n  // backdrop. 30% of the side is 9% of the area — well inside what the H\n  // error-correction level (30%) recovers.\n  var hole = Math.round(inner * 0.3), off = (inner - hole) / 2;\n  var ctx = cv.getContext('2d');\n  ctx.globalCompositeOperation = 'destination-out';\n  ctx.beginPath();\n  if (ctx.roundRect) ctx.roundRect(off, off, hole, hole, hole * 0.2); else ctx.rect(off, off, hole, hole);\n  ctx.fill();\n  logo.style.width = logo.style.height = (hole * 0.8 / dpr) + 'px';\n}\n\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n","params":{"bg":{"type":"string","default":"transparent","maxLen":32,"description":"Background colour, any CSS colour. Left alone the image stays transparent, so the code drops onto any backdrop — make sure that backdrop is light. Set it (bg=%23ffffff) when you need an opaque code."},"domain":{"type":"string","required":true,"maxLen":512,"description":"What the code encodes. A bare host (media0.io) is encoded as https://media0.io; a value that already carries a scheme (https://…, mailto:…, tel:…) is encoded verbatim."},"fg":{"type":"string","default":"#000000","maxLen":32,"description":"Module colour, any CSS colour. Keep it dark: scanners need strong contrast against whatever sits behind the code."},"logo":{"type":"image_url","maxLen":500,"description":"Optional centre logo: an http(s) URL to a PNG, JPEG, WebP, GIF or AVIF (no SVG). media0 fetches it server-side, so nothing but the URL travels in the query. A square image works best."}}}}
