{"id":"adddf470-9c72-48ab-8a6c-aeb08e37e9ca","name":"WhatsApp QR Code","slug":"whatsapp-qr-code","category":"qr-codes","description":"A WhatsApp QR code as a plain image URL: pass a phone number, get a crisp 1024x1024 code on a transparent background with the WhatsApp mark punched into the middle. Scanning it opens a chat with that number — no wa.me link to type, no screenshot to crop.","sample_params":{"bg":"#ffffff","phone":"+49 151 2345 6789"},"spec":{"name":"whatsapp-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#badge{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);border-radius:50%;\n  background:linear-gradient(160deg,#2ee06f,#12a85e);display:flex;align-items:center;justify-content:center}\n#badge svg{fill:#fff;display:block}\n\u003c/style\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003cdiv id='qr'\u003e\u003c/div\u003e\n\u003cdiv id='badge'\u003e\n  \u003csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'\u003e\u003cpath d='M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413Z'/\u003e\u003c/svg\u003e\n\u003c/div\u003e\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 the || ''.\nvar RAW = String({{.phone}} || '').trim();\nvar FG = {{.fg}} || '#000000';\nvar BG = {{.bg}} || 'transparent';\n\n// wa.me wants the plain international number: no +, no 00 trunk, no\n// spaces, dashes or brackets.\nvar payload = 'https://wa.me/' + RAW.replace(/[^0-9]/g, '').replace(/^00/, '');\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\n// Punch a round hole through the modules rather than covering them, so on\n// a transparent background the mark sits on the consumer's own backdrop.\n// 30% of the side is 9% of the area — well inside what the H\n// error-correction level (30%) recovers.\nvar hole = inner * 0.3, c = inner / 2;\nvar ctx = cv.getContext('2d');\nctx.globalCompositeOperation = 'destination-out';\nctx.beginPath();\nctx.arc(c, c, hole / 2, 0, 2 * Math.PI);\nctx.fill();\n\nvar badge = document.getElementById('badge'), d = hole * 0.86 / dpr;\nbadge.style.width = badge.style.height = d + 'px';\nvar glyph = badge.querySelector('svg');\nglyph.style.width = glyph.style.height = (d * 0.56) + 'px';\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."},"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. The centre mark stays WhatsApp green."},"phone":{"type":"string","required":true,"maxLen":32,"description":"The WhatsApp number in international form, e.g. +49 151 2345 6789. Spaces, dashes, brackets and a leading + are ignored, and a 00 trunk prefix is dropped, so the code always encodes https://wa.me/\u003cnumber\u003e."}}}}
