QR-Code mit zentriertem Marken-Logo
QR-Codes tragen eingebaute Reed-Solomon-Fehlerkorrektur. Das Level 'H' erholt sich von ~30% Beschädigung — genug, um ein kleines Loch in der Mitte für ein Marken-Logo zu stanzen, ohne dass der Code seine Lesbarkeit verliert. Pipeline: QR via `qrcode`-Extension auf ECL=H erzeugen, Logo auf ~22% QR-Breite skalieren (sicher unter dem 30%-Recovery-Limit), auf einer weißen Scheibe platzieren damit es bewusst wirkt und nicht wie ein Schaden, dann via Porter-Duff Over auf die QR-Mitte komponieren. LOGO_PCT näher ans Limit drücken oder DARK / LIGHT für Markenfarben anpassen.
LOGO
QR with logo
JavaScript
// QR code with a centred logo — link to mlcgo.eu
// demo_qrcode.js
//!INPUT: LOGO
//!OUTPUT: OUTPUT
//!PARAM: URL:string=https://mlcgo.eu
//!PARAM: SCALE:number=12,min=4,max=24
//!PARAM: ECL:string=H
//!PARAM: BORDER:integer=2,min=0,max=8
//!PARAM: LOGO_PCT:number=22,min=10,max=30
//!PARAM: LOGO_FRAME:integer=8,min=0,max=24
//!PARAM: DARK:string=#0f172a
//!PARAM: LIGHT:string=#ffffff
// QR codes carry built-in Reed-Solomon error correction. The "H" level
// recovers ~30% of the symbol, which means we can punch a small hole
// out of the centre — for a brand logo — and the code still scans.
//
// Pipeline:
// 1. Generate the QR code via the `qrcode` engine extension at
// ECL=H. Choose colours and module-pixel size up front so the
// rasterised result is final.
// 2. Resize the logo to LOGO_PCT% of QR width. Keep it well under
// 30% to leave a margin against the recovery limit; 22% is a
// conservative sweet spot.
// 3. Build a white "puck" frame (logo + 2 × LOGO_FRAME px padding)
// so the logo sits on a clean background and the eye reads it
// as intentional, not as damage to the code.
// 4. Composite logo onto the puck via Porter-Duff "Over" (alpha-
// aware), then composite the whole puck onto the QR centre.
const qr = Engine.qrcode(URL, {
scale: SCALE,
ecl: ECL,
border: BORDER,
light: LIGHT,
dark: DARK,
});
// Logo: load, then resize to LOGO_PCT of QR width.
const logoSize = Math.round((qr.width * LOGO_PCT) / 100);
const logo = Engine.loadImage(LOGO).resize(logoSize, logoSize);
// White puck behind the logo. Engine.createImage returns a
// transparent-black canvas; adjustAlpha(1, Set) → fully opaque, then
// tint("#fff", 1.0) blends every pixel toward pure white.
const puckSize = logoSize + 2 * LOGO_FRAME;
const puck = Engine.createImage(puckSize, puckSize)
.adjustAlpha(1.0, AlphaMode.Set)
.tint("#ffffff", 1.0);
// Compose logo onto puck (Porter-Duff Over), then puck onto QR
// centre. blendAt takes a Px (= {x, y}) — use the global `px(x, y)`
// helper. Parallelises across rows since 1.29.11, so even big QR
// codes composite in a single frame.
puck.blendAt(logo, px(LOGO_FRAME, LOGO_FRAME), 1.0, BlendMode.Over);
const cx = Math.floor((qr.width - puckSize) / 2);
const cy = Math.floor((qr.height - puckSize) / 2);
qr.blendAt(puck, px(cx, cy), 1.0, BlendMode.Over);
qr.save(OUTPUT);
// © 2026 Michael Lechner · mlc OpticScript · https://mlcgo.eu · Elastic License 2.0