Benchmarks

Performance benchmarks measured on Apple M-series, Node.js, single-threaded. 773 tests across 24 test files verify correctness across all configurations.

Encoding Pipeline

Operationp50p95ops/s
encode (string → bits)0.01ms0.02ms118,000+
rsEncode (payload → codeword)<0.01ms<0.01ms452,000+
rsDecode (no errors)<0.01ms<0.01ms400,000+
rsDecode (2 byte errors)0.02ms0.06ms41,000+
decode (bits → string)0.01ms0.01ms118,000+
bytesToBits / bitsToBytes<0.01ms<0.01ms347,000+

Encoding and decoding are sub-millisecond — fast enough to run inline on every keystroke without any perceptible delay.

Rendering

Operationp50p95ops/s
renderSVG (300px)0.02ms0.03ms50,000+
renderSVG (600px)0.02ms0.02ms57,000+

SVG rendering is resolution-independent and extremely fast. The renderer merges consecutive 1-bits into single arc strokes, keeping the SVG path count low.

Scan Pipeline

Each stage of the scanning pipeline, measured independently:

Operationp50p95ops/s
toGrayscale (300×300)0.24ms0.26ms4,100+
solveHomography0.02ms0.02ms62,000+
warpPerspective (→300px)1.73ms1.94ms577+
refineCenterFromDot0.27ms0.30ms3,700+
analyzeOrientation0.84ms0.94ms1,187+
validateCircularCode0.26ms0.30ms3,800+
scoreFrame0.51ms0.64ms1,968+
samplePolarGrid0.07ms0.16ms13,400+
detectCircle (Hough, 320px)6.41ms6.65ms156+

The pipeline bottleneck is warpPerspective — bilinear interpolation over ~90K pixels. Hough circle detection is the slowest individual stage but is only used as a fallback when the ML model is not loaded.

End-to-End

Operationp50p95ops/s
scanFrame (known detection)3.4ms4.4ms~297
Full roundtrip (encode → render → rasterize → scan)3.9ms5.0ms~259

With known detection (skipping Hough), the full decode pipeline runs at ~297 fps — nearly 10x faster than the 30 fps camera rate. This leaves ample headroom for frame scoring, consensus voting, and UI rendering.

Capacity

Grid capacity in characters by configuration. Byte mode shown first, alphanumeric in parentheses. ECC = 2 bytes (corrects 1 byte error):

Rings32 segments48 segments
45 (6)10 (14)
58 (10)13 (18)
610 (14)16 (22)
712 (16)20 (28)
814 (20)23 (32)

With ECC = 8 bytes (corrects 4 byte errors), subtract 6 from the byte-mode column. Numeric mode is even more efficient — approximately 2.4x the byte-mode capacity.

Auto-Sizing

When encode() is called without explicit parameters, it auto-selects the optimal configuration. Here's what it picks for common inputs:

InputRingsSegmentsECCUsed/Capacity
"Hi"448896/112 bits
"hello"4488112/112 bits
"1234567890"4487112/112 bits
"https://example.com"6484160/160 bits

The auto-sizer minimizes rings first (smallest visual code), then maximizes ECC with the remaining capacity. It searches across segment candidates [32, 48] and constrains rings to [4, 8] and ECC to [2, 8] bytes.

Test Coverage

AreaTests
Encoding / decoding / modes80+
Auto-sizing (all permutations)40+
Layout geometry34
Reed-Solomon codec48
SVG rendering14
Scan pipeline406
Benchmarks21
End-to-end roundtrips130+
Total773