| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| README.md | 2026-05-08 | 3.5 kB | |
| v1.9.0 source code.tar.gz | 2026-05-08 | 199.8 kB | |
| v1.9.0 source code.zip | 2026-05-08 | 231.9 kB | |
| Totals: 3 Items | 435.3 kB | 1 | |
Highlights
4–5× faster tm.get() on Chromium with byte-identical fingerprint output vs 1.8.1. The win is largest on CPU-constrained devices: mid-tier mobile (Lighthouse 4× throttle) goes from ~210 ms → ~67 ms; low-tier mobile (6×) ~330 ms → ~100 ms.
| metric | 1.8.1 | 1.9.0 | delta |
|---|---|---|---|
| local total median (Chromium) | 117.7 ms | 19.6 ms | -83% |
| CPU-throttled 8× total median | ~625 ms | 136 ms | -78% |
What changed
- WebGL: module-scope cache of canvas + GL context + program + buffer (eliminates per-call shader compile + program link); context-loss recovery via
webglcontextlostlistener with{ once: true }semantics; defense-in-depth cache-clear in renderImage's catch block. - commonPixels: short-circuit length-1, inline 3-way for length-3 (eliminated 80,000+ allocations per call on the common path).
- raceAllPerformance: timer cleanup (was leaking 13 setTimeout callbacks per
tm.get()). - stableStringify: O(1) Set-based cycle detection (replaces O(N) array indexOf).
- system.getBrowser: memoized (~12 sequential UA regex matches → 1 cached result).
- audio: removed stray
console.errorfrom production hot path. - WebRTC: dropped the ICE-candidate wait (with
iceServers: []only host candidates ever fire); fixed an outer-catch resource leak.
New observability (gated on options.performance: true)
The result.elapsed map now also includes:
_pipeline.dispatch/_pipeline.resolve/_pipeline.filter/_pipeline.stringify/_pipeline.hash/_pipeline.assembly— per-phase timings insidegetThumbmark._dispatch.<componentName>— per-component sync-prelude timing (the work that runs in the component function before its first await yields).
These have no effect when options.performance is false (the default). They're _-prefixed to signal observability surface — useful for diagnosing perf in the wild. The signature change is what motivated the minor (1.9.0) rather than patch bump.
Hash compatibility
Verified byte-identical fingerprint output vs 1.8.1 on Chromium and Samsung Internet. Two narrow exceptions, both intentional and safe:
- WebRTC: users whose previous component returned
{ ..., timeout: true }(the timeout path, fired on slow-network conditions) will see a one-time hash transition to the deterministic{ ..., candidateType: 'host' }. These users were already getting non-deterministic fingerprints; they now get a stable one. - WebGL caching: cache is OFF for Brave (preserves Brave's per-context noise). Tor / Firefox-RFP typically disable WebGL or return constants — caching gives byte-identical output in those cases.
No breaking changes
The public class (Thumbmark) and free function (getThumbmark) APIs are unchanged.
Cross-browser perf (1.9.0, full mode, 50 iter)
| browser | local total median |
|---|---|
| Chromium | 24.65 ms |
| Webkit | 10.00 ms |
| Firefox | 227.50 ms |
Firefox's slowness is driven by the audio component's OfflineAudioContext setup (~166 ms in dispatch). This is pre-existing, not introduced by this release; it's the next optimization target.
Full report
See docs/perf-optimization-report.md in the repo for the full session methodology, hash-stability discipline, and follow-ups not pursued.
Install
npm install @thumbmarkjs/thumbmarkjs@1.9.0
CDN:
https://cdn.jsdelivr.net/npm/@thumbmarkjs/thumbmarkjs@1.9.0/dist/thumbmark.umd.js