This was fun: A "Where Is Waldo" game for QR codes.

Can you find it? Your phone can!


The basic idea

QR scanners don't see color. The first thing a decoder does is convert the image to grayscale — collapsing each pixel to a single brightness value via roughly 0.299·R + 0.587·G + 0.114·B — then binarize and hunt for the finder patterns (the three corner squares) and module grid. Everything downstream operates on luminance alone.

That gap is the whole trick: luminance carries the code; hue and saturation are free to do whatever you want. You have two perceptual channels the scanner is blind to, and humans hunting for a pattern rely heavily on exactly those channels.

So the code is rendered as a luminance difference — "dark" modules low-luma, "light" modules high-luma — but each module is painted a vivid random color at its assigned brightness. The key subtlety is that a fully saturated color's true luminance depends on its hue: pure blue sits around 0.11, pure red around 0.30, pure yellow near 0.89. So "dark" modules don't have to be dark and muddy — you can use electric blues, reds, and violets that are naturally low-luminance at full saturation, and reserve the bright hues (yellow, cyan, green) for "light" modules.

To the hyman eye it's a saturated confetti, but converted to grayscale it's a scannable QR.


The code

#! /usr/bin/env python

import cv2
import numpy as np
PAYLOAD = "https://blog.vrypan.net/2026/07/07/where-is-qrdo/"
MOD = 8
GRID_W, GRID_H = 192, 96
N_DECOYS = 180
enc = cv2.QRCodeEncoder.create()
qr = enc.encode(PAYLOAD)[2:-2, 2:-2]
n = qr.shape[0]
det = cv2.QRCodeDetector()
LUMA = np.array([0.114, 0.587, 0.299])   # BGR
# --- curated DARK palette (BGR), all low-luma, balanced across families ----
# reds need V<255 to stay low-luma; blues/violets are low-luma at full V.
DARK = np.array([
    [0,   0,   220],   # strong red
    [0,   0,   180],   # deep red
    [30,  0,   200],   # red-magenta
    [255, 0,   0],     # pure blue
    [200, 0,   0],     # deep blue
    [255, 40,  0],     # blue-cyan lean
    [200, 0,   120],   # indigo
    [180, 0,   180],   # violet
    [140, 0,   200],   # purple-red
], np.float32)
# --- LIGHT palette (BGR), all high-luma, vivid + bright ---
LIGHT = np.array([
    [0,   255, 255],   # yellow
    [0,   255, 150],   # yellow-green
    [0,   255, 0],     # green
    [255, 255, 0],     # cyan
    [255, 255, 150],   # pale cyan
    [150, 255, 255],   # pale yellow
    [200, 255, 200],   # mint
], np.float32)
print("dark palette lumas:", np.round(DARK @ LUMA / 255, 2))
print("light palette lumas:", np.round(LIGHT @ LUMA / 255, 2))
def build(seed):
    r = np.random.default_rng(seed)
    # --- random QR placement (keep a 1-module margin for the finder halos) ---
    qx = int(r.integers(1, GRID_W - n))
    qy = int(r.integers(1, GRID_H - n))
    is_dark = r.random((GRID_H, GRID_W)) < 0.40
    for cx, cy in [(qx, qy), (qx+n-7, qy), (qx, qy+n-7)]:
        is_dark[cy-1:cy+8, cx-1:cx+8] = False
    for y in range(n):
        for x in range(n):
            is_dark[qy+y, qx+x] = qr[y, x] < 128
    for _ in range(N_DECOYS):
        dx = int(r.integers(1, GRID_W-9)); dy = int(r.integers(1, GRID_H-9))
        if (qx-10 < dx < qx+n+1) and (qy-10 < dy < qy+n+1):
            continue
        s = int(r.integers(6, 9))
        is_dark[dy:dy+s, dx:dx+s] = True
        is_dark[dy+1:dy+s-1, dx+1:dx+s-1] = False
        inner = int(r.integers(1, 3))
        is_dark[dy+2:dy+s-2-(inner-1), dx+2:dx+s-2-(inner-1)] = True
    canvas = np.zeros((GRID_H, GRID_W, 3), np.float32)
    di = r.integers(0, len(DARK), (GRID_H, GRID_W))
    li = r.integers(0, len(LIGHT), (GRID_H, GRID_W))
    for y in range(GRID_H):
        for x in range(GRID_W):
            canvas[y, x] = DARK[di[y, x]] if is_dark[y, x] else LIGHT[li[y, x]]
    img = cv2.resize(canvas.astype(np.uint8), (GRID_W*MOD, GRID_H*MOD),
                     interpolation=cv2.INTER_NEAREST)
    return img, (qx, qy)
import secrets
# random search order (fresh OS entropy each run) so the QR lands in a
# different spot every time, rather than always picking the first seed that works
seeds = list(range(1, 4000))
np.random.default_rng(secrets.randbits(64)).shuffle(seeds)
for seed in seeds:
    img, (qx, qy) = build(seed)
    if det.detectAndDecode(img)[0] == PAYLOAD:
        cv2.imwrite("qr_redblue.png", img)
        print(f"seed {seed}: decodes OK -> qr_redblue.png  (QR at module {qx},{qy})")
        break
else:
    raise SystemExit("no working seed found")