From 80608602ad09d05ee79e7c1cc70130a0774f4c29 Mon Sep 17 00:00:00 2001 From: Dave Date: Sun, 4 Oct 2026 13:51:44 +0200 Subject: [PATCH] fix(scan): the QR was being downscaled below its own resolution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scanning felt like it never resolved. It was not slow — it was being downscaled into unreadability. The printed QR is 28pt on a 210mm page: 4.7% of the page width. The loop down-scaled the camera frame to 640px wide before decoding, so a phone pointed at a full A4 sheet rendered that QR at ~30px total, about 1 pixel per module. Measured against the real rendered page, jsQR cannot read it at any crop size: cap px/module whole frame bottom-45% ROI 640 1.04 MISS MISS 1280 2.08 miss miss 1600 2.60 HIT 69ms HIT 28ms 1920 3.12 HIT 98ms HIT 39ms 2560 4.16 HIT 171ms HIT 39ms Above ~2.5px per module it is reliable, so the cap is now 1920. No amount of speeding up the loop could have fixed this; the pixels were not there. - requestAnimationFrame instead of a 120ms setInterval. rAF fires straight after a painted frame, so the decoder gets a fresh one rather than whichever frame happened to be current at the tick; it also removes the 120ms floor on every detection and lets a readyState stall recover on the next frame instead of a timer tick. - Two regions instead of one. The delivery-note QR sits in the bottom band of the page, so that band is scanned every frame at roughly a third of the cost of the full frame (39ms vs 98ms). The full frame runs every 6th as a safety net for a tilted page. It is an accelerator, not a restriction. - Native BarcodeDetector is now EXCLUSIVE when present. It used to fall through to jsQR on a miss "because jsQR sometimes succeeds where native blurs", which doubled the cost on every miss — the common case. The native detector is Google's own ML stack; when the browser ships it, use it alone. - Screen wake lock while scanning, re-taken on visibilitychange because the browser drops it whenever the tab hides. Otherwise the phone locks mid-queue. - 3s debounce per code, and the camera keeps running after a hit, so a stack of notes can be worked through without a tap per sheet. The viewfinder is now ONE