quadscan

API Reference

Single class, two static convenience methods, three custom errors.

class Quadscan

import { Quadscan } from 'quadscan';

const q = new Quadscan(options?);

A Quadscan instance holds an onnxruntime-web InferenceSession. Keep one warm for batch / real-time work; or skip the constructor entirely and use the static Quadscan.scan / Quadscan.extract for one-offs (they share a lazy internal singleton).

Constructor options: QuadscanOptions

OptionTypeDefaultDescription
model'lcnet' | 'fastvit''lcnet'Built-in preset. LCNet (4.5 MB) is fast and accurate; FastViT (13 MB) is more accurate on hard cases.
modelUrlstringresolved from modelOverride the model URL entirely. Useful for self-hosting (see Self-hosting recipe).
normalization'zero-one' | 'minus-one-one'inferred from URLInput normalization. LCNet wants zero-one, FastViT wants minus-one-one. The library guesses correctly for the bundled models; override if you bring a custom one.
executionProvidersArray<'webgpu' | 'wasm'>['webgpu', 'wasm']ORT execution providers, tried in order. The runtime falls back to the next if a provider isn’t supported.
wasmPathsstringjsdelivr CDNWhere onnxruntime-web looks for its .wasm files. Override to self-host.

Instance methods

async initialize(): Promise<void>

Eagerly download the model and create the ORT session. Optional; scan() and extract() do this on first call. Call it during app startup if you want to amortize the load.

async scan(input, options?): Promise<ScanResult>

Detect document corners; optionally perspective-warp.

input is one of:

TypeNotes
HTMLImageElementMust be loaded (complete && naturalWidth > 0).
HTMLCanvasElementAny size.
HTMLVideoElementCurrent frame; works while playing.
ImageBitmapDecoded off-thread; great for workers.
ImageDataRaw pixels.
Blob | FileAuto-decoded via createImageBitmap.

async extract(input, corners, options?): Promise<ScanResult>

Perspective-warp input using user-supplied corners, skipping detection. Does not invoke the model, does not download it either. Use this when corners come from manual UI adjustment, a prior scan, or another detector.

dispose(): void

Release the ORT session. Subsequent scan() calls re-initialize. Idempotent.

[Symbol.dispose]()

Enables using q = new Quadscan() syntax (TypeScript 5.2+).

Static methods

MethodNotes
Quadscan.scan(input, options?)One-shot via internal singleton.
Quadscan.extract(input, corners, options?)One-shot warp via singleton. Does not load the model.
Quadscan.disposeShared()Dispose the internal singleton. Idempotent.

ScanOptions

Per-call options for scan().

OptionTypeDefaultDescription
mode'detect' | 'extract''detect'detect returns corners only (no warp); extract also returns the warped image in output.
output'canvas' | 'imagedata' | 'dataurl''canvas'Warp output format.
maxOutputDimensionnumber2048Cap on the longer side of the warped output.
minDetectionConfidencenumber0.1Detection floor; if peak heatmap confidence is below this, returns { success: false }.
signalAbortSignalnoneCancels scan; throws DOMException('AbortError').
onProgress(e: ProgressEvent) => voidnoneLifecycle progress events (see below).
debugbooleanfalsePopulate result.debug with the raw heatmap and pre-reorder corners.

ProgressEvent

type ProgressEvent =
  | { phase: 'model-download'; loaded: number; total: number }
  | { phase: 'model-compile' }
  | { phase: 'preprocess' }
  | { phase: 'inference' }
  | { phase: 'decode' }
  | { phase: 'warp' };

model-download is the only event with progress; the rest are lifecycle markers. total may be 0 if the server didn’t send Content-Length.

ScanResult

interface ScanResult {
  success: boolean;
  message: string;
  confidence: number | null;
  output: HTMLCanvasElement | ImageData | string | null;
  corners: CornerPoints | null;
  timings: Timing[];
  debug: DebugInfo | null;
}
FieldWhen success: trueWhen success: false
confidencemin of the four corner confidences (0..1)null
outputthe warped image (mode extract) or null (mode detect)null
cornersTL/TR/BR/BL in source-image coordsnull
timingsper-step durationspartial (whatever ran)

Always check result.success before reading corners or output. In TypeScript, the result is structurally typed. Narrow with if (r.success) and treat r.corners as non-null inside.

CornerPoints

interface CornerPoint { x: number; y: number; confidence: number }
interface CornerPoints {
  topLeft: CornerPoint;
  topRight: CornerPoint;
  bottomRight: CornerPoint;
  bottomLeft: CornerPoint;
}

Coordinates are in source-image pixels (not normalized). confidence is the heatmap peak value at that corner.

For input to extract(), confidence is optional and defaults to 1.

Errors

All errors inherit from QuadscanError and carry a stable code:

ClasscodeThrown when
ModelLoadErrorMODEL_LOAD_FAILEDThe .onnx couldn’t be fetched, parsed, or compiled. Includes the URL and HTTP status in message; original error in cause.
InvalidInputErrorINVALID_INPUTinput is not a supported type, or corners is missing fields.
DegenerateQuadErrorDEGENERATE_QUADThe four corners can’t form a convex quad (e.g. > 45° rotation, collinear points). Rare.
import { Quadscan, ModelLoadError } from 'quadscan';
try {
  await Quadscan.scan(file);
} catch (err) {
  if (err instanceof ModelLoadError) {
    console.error('Model fetch failed:', err.message, err.cause);
  } else throw err;
}

Failed detection (the model ran but found no document) is not an error. It comes back as { success: false, message } in the ScanResult.

Exports

export { Quadscan } from 'quadscan';
export {
  QuadscanError, ModelLoadError, InvalidInputError, DegenerateQuadError,
} from 'quadscan';
export type {
  ScanResult, ScanOptions, ScanInput, ScanMode, OutputFormat,
  CornerPoint, CornerPoints, CornerPointInput, CornerPointsInput,
  QuadscanOptions, ModelPreset, ExecutionProvider, Normalization,
  ProgressEvent, Timing, DebugInfo,
} from 'quadscan';