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

83 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.