quadscan

Real-time webcam

For continuous detection (every N ms, or every animation frame) you want a warm instance (one Quadscan that holds the ORT session across calls). The static Quadscan.scan would also work, but exposing the instance lets you dispose() it when the user stops the camera.

mode: 'detect' skips the perspective-warp step (which can be the most expensive part on large frames). Draw the corners yourself on an overlay canvas; only warp when the user hits “capture”.

Pattern

import { Quadscan } from 'quadscan';

const video = document.querySelector('video')!;
const overlay = document.querySelector('canvas')!;
const ctx = overlay.getContext('2d')!;

const stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
await video.play();
overlay.width = video.videoWidth;
overlay.height = video.videoHeight;

const q = new Quadscan();
await q.initialize(); // download model up front while showing a spinner

const timer = setInterval(async () => {
  const r = await q.scan(video, { mode: 'detect' });
  ctx.clearRect(0, 0, overlay.width, overlay.height);
  if (!r.success) return;

  const { topLeft, topRight, bottomRight, bottomLeft } = r.corners!;
  ctx.strokeStyle = 'oklch(0.65 0.18 142)';
  ctx.lineWidth = 4;
  ctx.beginPath();
  ctx.moveTo(topLeft.x, topLeft.y);
  ctx.lineTo(topRight.x, topRight.y);
  ctx.lineTo(bottomRight.x, bottomRight.y);
  ctx.lineTo(bottomLeft.x, bottomLeft.y);
  ctx.closePath();
  ctx.stroke();
}, 500);

// Tear-down
addEventListener('beforeunload', () => {
  clearInterval(timer);
  for (const track of stream.getTracks()) track.stop();
  q.dispose();
});

Tips

  • Throttle to ~2 Hz. 500 ms is plenty for “frame the doc and hold still” UX; faster than that and you’ll fight your own queued scans.
  • Don’t call inside requestAnimationFrame unless you also gate it with an isBusy flag. Inference takes 30–80 ms on WebGPU.
  • Capture pipeline: when the user taps “scan”, do one final q.scan(video, { mode: 'extract' }) to get the warped output.
  • Confidence threshold: filter weak detections with minDetectionConfidence: 0.4 to avoid jittery overlays on empty scenes.

See the live webcam example for a complete implementation with start/stop controls and an FPS counter.