For task-oriented snippets, see Examples.
libcreep is ESM-only. It provides named exports and a frozen default export:
import LibCreep, { collect, getBrowserCapabilities, load } from 'libcreep';
LibCreep.load === load; // trueNamed exports are recommended because they make the API and bundle usage explicit. Importing the module does not read browser globals, so it is safe in SSR and Node-based build tools. Fingerprint collection itself requires a browser window.
The default object contains load, collect, hashComponents,
componentsToDebugString, getBrowserCapabilities, isBrowserEnvironment,
isFingerprintingSupported, algorithmVersion, and libraryVersion.
function load(options?: LoadOptions): Promise<FingerprintCollector>;load() verifies the minimum browser APIs, waits until document.body exists,
dynamically loads the detector runtime, and returns a reusable collector. Its
load-specific signal and timeoutMs options cover that readiness/import
phase; they do not apply to later calls on the returned collector.
import { load } from 'libcreep';
const collector = await load();
const result = await collector.collect();The collector exposes algorithmVersion, libraryVersion, and
collect(options?). Reuse a collector when an application collects more than
once.
All collections in one loaded module instance are serialized, including calls
made through different collectors and the one-shot collect() function. Some
detectors share temporary DOM and module state, so only one collection runs at
a time. A result's durationMs measures its active collection, not time spent
waiting in this queue.
interface LoadOptions {
debug?: boolean;
signal?: AbortSignal;
timeoutMs?: number;
worker?: {
strategy?: 'auto' | 'dedicated-only' | 'service-first';
url?: string | URL;
};
}| Option | Default | Behavior |
|---|---|---|
debug |
false |
After each successful collection, logs package/algorithm versions and formatted core component results. Development and test transforms also print detector timing tables; the production package excludes that profiler. Failed collections are not logged. |
signal |
none | Cancels document readiness and dynamic runtime loading. It does not govern later collector.collect() calls. |
timeoutMs |
none | Applies a 0 to 2,147,483,647 millisecond deadline to load() only. Invalid values reject with TypeError. |
worker.url |
./worker.js beside the package entry |
Overrides the module-worker asset location. Use this when a bundler or CDN relocates package assets. |
worker.strategy |
'auto' |
Controls the worker-scope fallback order described below. |
Worker strategies are:
'auto': try a shared module worker, then a dedicated module worker.'dedicated-only': use only a dedicated module worker.'service-first': try a temporary service worker, then shared and dedicated module workers.
Service-worker-first mode is opt-in because registration changes origin state
temporarily. It creates a unique scope and unregisters only the registration it
created. An unknown strategy causes load() to reject with a TypeError.
A worker is not a minimum requirement for the rest of the fingerprint. If all
selected worker transports fail, components.workerScope is unsupported and
collection continues. See Browser runtime and workers
for hosting, CSP, secure-context, and fallback details.
function collect(options?: CollectOptions): Promise<FingerprintResult>;
interface CollectOptions {
debug?: boolean;
includeWebRtc?: boolean;
signal?: AbortSignal;
timeoutMs?: number;
worker?: WorkerOptions;
}collect() is the one-shot convenience API. It separates the load options,
creates a collector, and performs one collection:
import { collect } from 'libcreep';
const result = await collect({
debug: true,
timeoutMs: 10_000,
worker: { strategy: 'auto' },
});interface CollectionOptions {
includeWebRtc?: boolean;
signal?: AbortSignal;
timeoutMs?: number;
}| Option | Default | Behavior |
|---|---|---|
includeWebRtc |
false |
Enables SDP, STUN, ICE-candidate, and address collection. This can disclose network information and contact external STUN servers. |
signal |
none | Rejects a queued or active call when aborted. An active abort also triggers cleanup for worker, WebRTC, audio, and temporary DOM resources. |
timeoutMs |
none | Aborts the entire operation after 0 to 2,147,483,647 milliseconds. Invalid values reject immediately with TypeError. |
For collector.collect(), the deadline includes its queue wait and active
detectors. For the one-shot collect() function, it additionally includes
waiting for document.body and dynamically loading the runtime. A timeout
rejects with a DOMException whose name is TimeoutError. A signal rejects
with signal.reason, or an AbortError DOM exception when no explicit reason
is available.
const controller = new AbortController();
try {
const result = await collector.collect({
signal: controller.signal,
timeoutMs: 5_000,
});
console.log(result.visitorId);
} catch (error) {
if (error instanceof DOMException && error.name === 'TimeoutError') {
console.warn('Fingerprint collection timed out');
} else {
throw error;
}
}Environment and orchestration failures reject the collection promise. Examples
include calling load() outside a browser, missing Web Crypto or TextEncoder,
cancellation, timeout, or a failure while calculating final hashes.
Detector-level failures are isolated. Every core and auxiliary entry uses the same discriminated union:
type ComponentResult<T> =
| { status: 'fulfilled'; value: T; durationMs: number }
| { status: 'unsupported'; durationMs: number }
| { status: 'rejected'; error: ComponentError; durationMs: number }
| { status: 'skipped'; durationMs: number };
interface ComponentError {
message: string;
name: string;
stack?: string;
}fulfilledcontains a successful value.unsupportedmeans the detector returned no usable value, commonly because its browser API or worker was unavailable.rejectedcaptures the detector's error without discarding other results.skippedmeans policy intentionally did not run an opt-in detector. WebRTC has this status by default.
Check status before accessing value or error:
const navigatorResult = result.components.navigator;
switch (navigatorResult.status) {
case 'fulfilled':
console.log(navigatorResult.value.platform);
break;
case 'rejected':
console.warn(navigatorResult.error.name, navigatorResult.error.message);
break;
case 'unsupported':
case 'skipped':
console.log(`Navigator result: ${navigatorResult.status}`);
}| Field | Meaning |
|---|---|
visitorId |
SHA-256 of an envelope containing algorithmVersion and the hardened stable component set. This is the preferred whole-fingerprint identifier. |
rawVisitorId |
Canonical SHA-256 of all core component statuses and fulfilled values. It intentionally includes unstable, high-entropy signals. |
fuzzyHash |
A 64-character Creep.js similarity signature. It is not a cryptographic identifier. |
bot |
Client-side bot-rule bit mask and the first matching rule, when any. |
components |
A result entry for every name in CORE_COMPONENT_NAMES. |
values |
Successful core values only, keyed by component name. Each value includes its component $hash. |
stableComponents |
A detached snapshot of the exact trust-filtered values hashed into visitorId; untrusted or unavailable groups may be absent or undefined. |
focusedHashes |
SHA-256 hashes for available narrower signal groups, such as Canvas image/text, WebGL, DOM rectangles, CSS, media types, and device/timezone data. |
auxiliary |
Results for mediaCapabilities, mediaDevices, status, and webRtc. Auxiliary values are not included in visitorId or rawVisitorId. |
durationMs |
Active collection duration in milliseconds. Detector durations can overlap because many probes run concurrently. |
algorithmVersion |
Version of the stable-ID inputs and canonical fingerprint algorithm. |
libraryVersion |
Installed npm package version. |
For fulfilled core entries, components[name].value and values[name] may
reference the same per-result snapshot; treat result data as immutable.
stableComponents is independently snapshotted, so mutating raw values cannot
change the data represented by the already-computed visitorId. Stable signal
groups omit the per-component $hash; those enrichment hashes are outputs, not
inputs to the stable identifier.
visitorId is hardened against known unstable or untrusted signals, but it is
not guaranteed to remain unchanged. Browser upgrades, privacy modes,
extensions, hardware changes, and detector availability can all affect it.
Persist algorithmVersion with identifiers so an application can make upgrade
decisions explicitly. Neither identifier should be used as authentication or
identity proof.
BOT_RULE_NAMES defines the eight-character bot.botMask order.
bot.firstMatchedRule is the first set bit in that order. The
excessiveLooseFingerprints and crowdBlendingScoreIsLow rules require
server-side history or crowd data and therefore remain unset in this
client-only package.
| Bit | Rule | Client-side meaning |
|---|---|---|
| 0 | liedWorkerScope |
The worker scope reported a detected lie. |
| 1 | liedPlatformVersion |
Worker UA platform data conflicts with font-derived OS information. |
| 2 | functionToStringHasProxy |
Function.prototype.toString exposes invalid or proxy-like behavior. |
| 3 | outsideFeaturesVersion |
Engine features fall outside the detected version range. |
| 4 | extremeLieCount |
Severe stealth signals, an unusual font/OS pairing, or more than 100 recorded lies were found. |
| 5 | excessiveLooseFingerprints |
Reserved for server-side history; always 0 here. |
| 6 | workerScopeIsBlocked |
No usable worker scope was collected. |
| 7 | crowdBlendingScoreIsLow |
Reserved for server-side crowd analysis; always 0 here. |
The stable component groups are navigator, screen, workerScope, media,
canvas2d, canvasWebgl, cssMedia, css, timezone,
offlineAudioContext, and fonts. Each is independently filtered, so do not
assume that all keys hold usable values.
Focused hash fields are:
interface FocusedHashes {
canvas2dEmoji?: string;
canvas2dImage?: string;
canvas2dPaint?: string;
canvas2dText?: string;
canvasWebglImage?: string;
canvasWebglParameters?: string;
clientRects?: string;
cssComputedStyle?: string;
cssSystem?: string;
deviceAndTimezone?: string;
mediaMimeTypes?: string;
}A focused hash is present only when its underlying signal is non-null and usable. A fulfilled but partial component does not receive hashes for optional fields it did not actually report.
function hashComponents(components: HashableComponents): Promise<string>;Hashes a custom component selection with the same canonical format used by
rawVisitorId:
import { hashComponents } from 'libcreep';
const customId = await hashComponents({
canvas2d: result.components.canvas2d,
canvasWebgl: result.components.canvasWebgl,
navigator: result.components.navigator,
});Component names and plain-object keys are sorted by UTF-16 code units, without
locale-sensitive collation. Fulfilled values are hashed;
non-fulfilled statuses are hashed by status. durationMs, rejected error text,
and stacks are ignored, so timing and browser-specific wording do not
destabilize the hash.
Fulfilled values may contain plain objects (including objects from another
window realm and objects whose keys use libcreep's reserved canonical $type
tag), arrays (including sparse arrays), JSON primitives, undefined, bigint,
non-finite numbers, negative zero, and genuine cross-realm Error objects.
Functions, symbols, cycles, symbol-keyed properties,
accessors, non-enumerable or extra array properties, and non-plain objects such
as Date are rejected
instead of being silently converted to lossy JSON. SHA-256 requires Web Crypto.
Returns indented JSON for diagnostics. Unlike hashComponents(), this output
retains statuses, durations, and captured error details. It also serializes
Error, bigint, negative zero, and non-finite numbers without the usual JSON
loss. It represents undefined object fields and array entries as
{ "$type": "undefined" } instead of dropping them or converting them to
null.
import { componentsToDebugString } from 'libcreep';
console.debug(componentsToDebugString(result.components));Debug output can contain detailed fingerprint and environment information. Redact it before sharing and do not log it by default in production.
interface BrowserCapabilities {
hasDedicatedWorker: boolean;
hasServiceWorker: boolean;
hasSharedWorker: boolean;
hasTextEncoder: boolean;
hasWebCrypto: boolean;
isBrowser: boolean;
isSecureContext: boolean;
}isBrowserEnvironment()checks for a browserwindowanddocumentonly.isFingerprintingSupported()checks the minimum collection requirements: a browser window,TextEncoder, and Web Crypto SHA-256.getBrowserCapabilities()reports those requirements plus secure-context and worker API availability. Capability flags report API presence, not a guarantee that CSP, permissions, origin rules, or asset loading will allow a particular detector to succeed.
A secure context is reported but is not itself a minimum check; it matters for service workers and several optional browser APIs.
LIBRARY_VERSIONis the installed package version.ALGORITHM_VERSIONis the fingerprint/canonical-hash version.CORE_COMPONENT_NAMESlists the 25 always-attempted core components.DEFAULT_AUXILIARY_COMPONENT_NAMESlists the three auxiliary probes run by default.OPT_IN_AUXILIARY_COMPONENT_NAMESlistswebRtc.AUXILIARY_COMPONENT_NAMEScombines both auxiliary manifests.BOT_RULE_NAMESdefines the bot-mask bit order.
The package exports its public TypeScript interfaces and type aliases,
including FingerprintResult, FingerprintCollector, component value types,
component result maps, options, capability types, bot types, and
HashableComponents. The generated declarations shipped in dist are the
authoritative field-level reference for detector values.