Files
archy/docs/qr-scanner-snappiness-handover.md
T
archipelagoandClaude Opus 5 cc00884b98 docs: repair links left dangling by the operations-doc removal
Untracking the ops docs broke every reference to them. Repoints or removes
those references across README, architecture, ROADMAP, the archive index,
lifecycle TESTING, the hardening plan, and the security docs — pointing at
the issue tracker where a live task list was meant, and dropping the entry
entirely where it only existed to link an internal file.

Also fixes two pre-existing broken links found by validating every relative
link in the tracked docs:
- README linked docs/OPEN_SOURCE_READINESS.md, which never existed
  (underscores vs hyphens).
- reticulum-daemon/README.md linked a local Claude session plan at
  ../../.claude/plans/enchanted-strolling-rocket.md — outside the repo, and
  a path that would have shipped publicly pointing at nothing.

All relative links in tracked markdown now resolve: 0 broken.
audit-secrets.sh still 5/5.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 10:10:25 -04:00

4.5 KiB
Raw Blame History

QR scanner snappiness — research + companion-dev handover

2026-07-29. Owner: web side = node repo (this doc's "web" items); native side = companion app dev (Mac). Backlog origin: "optimise companion QR scan (quicker start/decode, low-light)".

Where scanning happens today

Path Stack Used when
Web live scan nimiq qr-scanner 1.4.x over getUserMedia, in WalletScanModal.vue HTTPS browsers / secure contexts
Photo fallback <input capture> photo → BarcodeDetector if present, else qr-scanner.scanImage multi-pass (decodePhotoRobust) Plain-http (LAN) where getUserMedia doesn't exist
Native scan ArchipelagoQr JS bridge → companion's native scanner (0.5.22 fixed dense invoice QRs) Inside the companion app

What makes it feel slow (ranked)

  1. Camera cold-start — the stream starts only after the user reaches the scan pane; on phones getUserMedia + first frame is routinely 6001500ms, and the native path pays a similar CameraX bind + ML Kit model cold-start.
  2. Decode cadence — web live scan was capped at 4 scans/sec (WebView preview lagged at 10/s when decoding on the JS worker). A hand-held code therefore waits up to 250ms after it's already sharp and centered.
  3. Low light / focus hunting — no torch control anywhere; no explicit continuous-focus request. Dense LN invoices need sharpness more than resolution.
  4. Dense-QR decode budget — big bolt11/catalog QRs push the JS decoder hard; the native ML Kit path is far better at these (proven by 0.5.22).

Web side (node repo — can be done here)

  • DONE (2026-07-29): scan at 10/s when BarcodeDetector exists (Chrome/ Android WebView decode natively — cheap), keep 4/s only for the JS-worker fallback.
  • Pre-warm the camera: start getUserMedia the moment the modal opens (action pane), not when the scan pane is reached — hide the preview until needed. Saves the entire cold-start from the user's perceived timeline.
  • Torch toggle: qr-scanner exposes hasFlash()/turnFlashOn() — add a 🔦 button on the scan pane (it silently no-ops where unsupported).
  • Continuous focus + modest resolution: pass constraints { focusMode: 'continuous', width: { ideal: 1280 } } — 720p-class frames start faster AND decode faster than 1080p+, with no loss for QR density that matters to us.
  • Don't stop/start between panes: returning from amount → scan currently re-inits the scanner; keep the (paused) stream alive while the modal lives.

Native side (companion dev handover)

The ArchipelagoQr bridge overlay is the right architecture — these are tuning items inside the native scanner activity:

  1. Pre-warm CameraX + ML Kit: bind the camera provider and instantiate BarcodeScanning.getClient(...) when the WebView requests the overlay — or even when the wallet modal opens (add a ArchipelagoQr.prewarm() bridge method; the web side will call it if present). ML Kit's first-inference model load is 100300ms — pay it before the user aims.
  2. Restrict formats: BarcodeScannerOptions with FORMAT_QR_CODE only — skipping the other symbologies measurably cuts per-frame latency.
  3. Analysis resolution ≈ 1280×720 with ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST — never queue stale frames; decode the newest one only.
  4. Continuous autofocus + tap-to-focus on the preview, and a torch toggle (low-light was an explicit user complaint).
  5. zoomRatio nudge for small codes: if no hit after ~2s, step zoom to 1.5× — helps distant/small printed codes without user action.
  6. Success haptic + instant dismiss: vibrate on decode and close the overlay immediately; perceived speed is heavily back-loaded.
  7. Optional: ML Kit enableAllPotentialBarcodes off and skip inverted scans unless first pass fails (inverted QRs are rare; halves work).

Acceptance criteria

  • Cold open → first successful scan of a normal invoice QR in < 2s on the companion app, < 3s in a mobile browser.
  • Dense (700+ char) bolt11 QR decodes in < 1.5s once framed, both paths.
  • Dim-room scan succeeds with the torch toggle without leaving the scanner.

Verification notes for whoever implements

  • Measure with a timestamp log: overlay-requested → camera-first-frame → decode-success. The three deltas map 1:1 onto items above.
  • Web BarcodeDetector presence differs per WebView/Play-Services build — keep the JS-worker fallback path intact.