Make one instance and hand it your key.

Everything starts with createBrowserSdk. Initialize it once, then ask the instance for a decoder, a camera scanner or a scanline read. When your screen goes away, call destroy and it cleans up everything it made.

Decoding factories live on the instance only. The package root exports no decoder of its own.

scan.tsTypeScript
import { createBrowserSdk } from '@scanlatch/sdk';

const sdk = createBrowserSdk({ key: accountKey });
await sdk.initialize();

// still images
const decoder = await sdk.createDecoder();
const results = await decoder.decode(file, { symbologies: ['qr', 'ean13'] });

// live camera
const scanner = sdk.createCameraScanner(video, { onDetections: show });
await scanner.start();

sdk.destroy();

The instance is the thing you keep around.

Create it with your key and initialize it once, then ask it for a decoder, a camera scanner, a scanline read or detection feedback. Destroying the instance stops everything it made. Camera enumeration and the permission state are exported on their own as well, for use before an instance exists.

function

listCameras

listCameras(language?) → Promise<MediaDeviceInfo[]>

Enumeration never acquires a stream; labels may be empty until permission has been granted.

Parameters
NameTypeDescription
language?BrowserLanguage
function

cameraPermission

cameraPermission() → Promise<CameraPermission>

Unsupported Permissions API camera queries are unknown, never inferred by acquisition.

function

createFeedback

createFeedback(options?) → Feedback

Locally synthesized audio, with HTML media fallback; no remote assets or shared AudioContext.

Parameters
NameTypeDescription
options?FeedbackOptions
class

ScanlatchError

extends Error

Properties
NameTypeDescription
codestringRead-only.
cameraAccessbooleanRead-only.
Methods
NameTypeDescription
constructor(code: string, message: string, options?: ErrorOptions & { cameraAccess?: boolean; name?: string })
interface

FeedbackOptions

Properties
NameTypeDescription
audio?boolean | { enabled?: boolean; audioSessionType?: AudioSessionType | null; implementation?: AudioImplementation; backend?: AudioBackend }
vibration?boolean | number | readonly number[]
interface

Feedback

Methods
NameTypeDescription
play() → Promise<void>
destroy() → void
interface

BrowserSdkConfiguration

Properties
NameTypeDescription
id?string | nullLocal application label only; null clears it. Never persisted or transmitted.
language?BrowserLanguage
audioBackend?AudioBackend
interface

BrowserSdk

Properties
NameTypeDescription
stateBrowserSdkStateRead-only.
versionstringRead-only.
idstring | nullRead-only.
languageBrowserLanguageRead-only.
licenseStatusLicenseStatusRead-only.
Methods
NameTypeDescription
configure(configuration: BrowserSdkConfiguration) → void
initialize(signal?: AbortSignal) → Promise<void>
listCameras() → Promise<MediaDeviceInfo[]>
cameraPermission() → Promise<CameraPermission>
createDecoder(options?: ImageDecoderOptions) → Promise<ImageDecoder>
createCameraScanner(video: HTMLVideoElement | string, options: CameraOptions) → CameraScanner
decodeScanline(samples: Uint8Array | Uint8ClampedArray, options?: ScanlineOptions) → ScanlineDetection | null
createFeedback(options?: FeedbackOptions) → Feedback
destroy() → void
types
BrowserLanguage="en" | "es"
BrowserSdkState="new" | "initializing" | "ready" | "destroyed"
CameraPermission="granted" | "denied" | "prompt" | "unknown"
AudioSessionType="auto" | "ambient" | "playback" | "transient" | "transient-solo" | "play-and-record"
AudioImplementation=() => void | Promise<void>
AudioBackend="auto" | "web-audio" | "html-audio"

Hand it a picture, get back what it found.

A decoder accepts pixels, an image element, an ImageBitmap, a Blob, a File or a URL, and returns one detection per barcode it read: payload, format, geometry and how the text was resolved.

const

imageSymbologies

imageSymbologies = [ 'ean13', 'ean8', 'upca', 'upce', 'databar', 'databar-exp', 'code39', 'code93', 'code128', 'i25', 'codabar', 'qr', 'aztec', 'datamatrix', 'pdf417' ] as const
interface

ImagePoint

Properties
NameTypeDescription
xnumber
ynumber
interface

ImageDetection

Properties
NameTypeDescription
textstring
formatImageSymbology
bytesUint8Array
symbologyIdentifierstring
rawBytesUint8ArrayNative message after native checksum disposition, before Scanlatch framing presentation.
eciBytesUint8ArrayOwned standard ECI-protocol bytes; not Unicode re-encoded as bytes.
hasECIboolean
textStatus'decoded' | 'unknown-encoding'
contentType'text' | 'binary' | 'mixed' | 'gs1' | 'iso15434' | 'unknown-encoding'
readerInitializationboolean
sequence?{ index: number; total: number; id: string }
supplement?string
messageAppendboolean
quadrilateral[ImagePoint, ImagePoint, ImagePoint, ImagePoint]
boundingRect{ x: number; y: number; width: number; height: number }
decodeDurationMsnumberWorker decode duration in milliseconds, excluding acquisition and queueing.
inputReceivedAtnumberUnix milliseconds when decode() accepted the input; not camera exposure time.
interface

ImageFormatRule

Properties
NameTypeDescription
formatImageSymbology
minLength?numberBounds on delivered bytes, after framing and checksum disposition. When omitted, ITF requires at least 6 and Codabar at least 2; shorter reads are mostly false positives.
maxLength?number
quietZone?numberRequired exterior margin in module widths, on both sides of a linear symbol.
checksum?ChecksumDisposition
alphabet?'basic' | 'extended'Code 39 shift pairs are literal in basic mode.
roundedFinders?boolean
supplements?'ignore' | 'read' | 'require'Retail add-on symbols are reported separately from the base payload.
profile?'itf14'ITF-14 requires the GTIN check digit, regardless of optional-check policy.
curved?boolean
interface

ImageDecodeOptions

Properties
NameTypeDescription
symbologies?readonly (ImageSymbology | ImageFormatRule)[]Omitted or empty enables all fifteen families.
inverted?boolean
maxDetections?numberInteger from 1 to 255; defaults to 1.
minimumScanlines?numberDistinct source scanlines required for linear decoding. Defaults to 2.
signal?AbortSignal
interface

ImageDecoderOptions

Properties
NameTypeDescription
wasmUrl?string | URL
signal?AbortSignalCancels initialization only; the SDK instance owns the returned decoder's lifetime.
types
ImageSymbology="ean13" | "ean8" | "upca" | "upce" | "databar" | "databar-exp" | "code39" | "code93" | "code128" | "i25" | "codabar" | "qr" | "aztec" | "datamatrix" | "pdf417"
ChecksumDisposition="none" | "strip" | "retain"
ImageInput=string | ImageData | HTMLImageElement | ImageBitmap | Blob | File

Point the camera and let it run.

You supply a video element and a detection callback. The scanner owns the stream, the frames, the camera switch and the torch and focus controls, and calls you back whenever it reads something.

interface

ScanRegion

Fractions of the displayed video content box; each inset is in [0, 0.5]. Capture keeps the visible frame. A detection is kept only when its center lies inside these insets.

Properties
NameTypeDescription
top?number
right?number
bottom?number
left?number
interface

CameraCapabilities

Properties
NameTypeDescription
torchboolean
focusboolean
interface

CameraOptions

extends FrameOptions

Properties
NameTypeDescription
onDetections(detections: ImageDetection[]) => void
onError?(error: Error) => void
decoder?ImageDecoderBorrowed decoders are never destroyed by the camera controller.
constraints?MediaTrackConstraints
resolution?'auto' | 'hd' | 'full-hd'
rememberCamera?boolean | stringTrue uses the default local-storage key; a string selects an application key.
imageOptions?Omit<ImageDecodeOptions, 'signal'>
frameIntervalMs?number
duplicateIntervalMs?number
hysteresis?{ count: number; intervalMs: number }
mode?'continuous' | 'triggered'
triggerDurationMs?number
feedback?FeedbackOptions
language?'en' | 'es'
region?ScanRegionfrom FrameOptions
backend?'auto' | 'cpu' | 'webgl2'from FrameOptions
readback?'auto' | 'sync' | 'deferred'from FrameOptions
chromaReject?booleanApply filterChromaticDetections to the original captured pixels before remapping results.from FrameOptions
interface

CameraScanner

Properties
NameTypeDescription
state'new' | 'initializing' | 'ready' | 'running' | 'destroyed'Read-only.
visiblebooleanRead-only.
backend'cpu' | 'webgl2' | undefinedRead-only.
readback'sync' | 'deferred' | undefinedRead-only.
readbackReasonstring | undefinedRead-only.
Methods
NameTypeDescription
initialize() → Promise<void>
start() → Promise<void>
stop() → void
destroy() → void
switchCamera(deviceId: string) → Promise<void>
setVisible(visible: boolean) → void
trigger() → void
getCapabilities() → CameraCapabilities
setTorch(enabled: boolean) → Promise<void>
focus(point: { x: number; y: number }) → Promise<void>

Buttons and outlines on top of your video.

Optional. It draws a viewfinder, marks what was found, and can add a camera picker, a torch toggle and tap to focus. It never changes how the scanner is configured.

function

createCameraOverlay

createCameraOverlay(container, scanner, video, options?) → CameraOverlay

An opt-in presenter in an isolated shadow root. The container and video stay caller-owned. Placement follows the video's viewport, object fit/position and supported axis-aligned transforms. Create once, forward onDetections to update(), and destroy independently of the scanner.

Parameters
NameTypeDescription
containerHTMLElement
scannerCameraScanner
videoHTMLVideoElement
options?CameraOverlayOptions
interface

CameraOverlayTheme

Properties
NameTypeDescription
primaryColor?stringColors accept only sRGB #RRGGBB or #RRGGBBAA. Defaults use neutral black/white.
detectionFillColor?string
detectionBorderColor?string
targetingLineActiveColor?string
maskColor?string
detectionBorderWidth?numberFinite CSS-pixel width clamps to [0, 4], default 0 (no outline).
viewfinderBorderWidth?numberFinite CSS-pixel width clamps to [1, 20], default 1.
cornerRadius?numberFinite general/control corner radius clamps to [0, 8], default 0.
viewfinderCornerRadius?numberOverrides the general radius; clamps to [0, 20], then half the shorter side.
interface

CameraOverlayOptions

Properties
NameTypeDescription
language?BrowserLanguageExplicit language wins over SDK language, then browser language, then English.
sdk?Pick<BrowserSdk, 'language' | 'listCameras'>
cameras?boolean | ((camera: MediaDeviceInfo) => boolean)False hides the selector; a predicate filters enumerated video inputs. Never requests permission.
torch?boolean
tapFocus?boolean
trigger?booleanEnable for a scanner configured with mode:'triggered'. Default false.
targetingLine?boolean
detections?booleanDetection visuals are enabled by default.
detectionDurationMs?numberFinite display lifetime clamps to [0, 60000] milliseconds, default 1000.
region?ScanRegionPass the same region as CameraOptions.region; the presenter cannot change scanner configuration.
logo?{ url: string; alt: string }HTTP(S), blob or data:image URLs; alt must be nonempty. Caller owns URL lifetime.
theme?CameraOverlayTheme
onError?(error: Error) => void
interface

CameraOverlay

Methods
NameTypeDescription
update(detections: ImageDetection[]) → voidReplaces displayed detections. Geometry is copied; caller-owned results are never mutated.
destroy() → voidRemoves only owned DOM/listeners. Does not stop scanning, tracks, or the caller's video.

For when you already have a row of pixels.

Reads the retail formats from a single line of grayscale samples, on the calling thread, with no worker and nothing to download.

interface

ScanlineOptions

Properties
NameTypeDescription
symbologies?readonly RetailSymbology[]Omitted or empty enables all four retail formats.
inverted?booleanAlso recognize light bars on a dark background.
interface

ScanlineDetection

Properties
NameTypeDescription
datastringIncludes the check digit; UPC-E also includes its number-system digit.
typeNameRetailSymbology
startnumberIncreasing, half-open pixel bounds in the original input, excluding quiet zones.
endnumber
direction"forward" | "reverse"
types
RetailSymbology="ean13" | "ean8" | "upca" | "upce"

How the SDK tells you where your key stands.

Read the status off the instance at any time. When a check refuses, the SDK throws a LicenseError carrying the reason.

class

LicenseError

extends Error

Properties
NameTypeDescription
codeLicenseFailureRead-only.
Methods
NameTypeDescription
constructor(code: LicenseFailure)
interface

LicenseStatus

Properties
NameTypeDescription
state'new' | 'checking' | 'trial' | 'paid-online' | 'paid-offline' | 'blocked' | 'protocol-error' | 'destroyed'Read-only.
installationId?stringRead-only.
trialEndsAt?numberRead-only.
reason?LicenseFailureRead-only.
types
LicenseFailure="destroyed" | "not-initialized" | "unavailable" | "protocol-error" | "storage" | "expired" | "aborted" | "origin-not-registered" | "trial-expired" | "trial-required" | "subscription-inactive" | "payment-required"

Rather be walked through it?

The setup guide takes you from account to a working camera in about ten minutes.