How to read a CreepJS result, section by section
You open CreepJS, the page fills with hashes, percentages and a red list of "lies", and it is not obvious which part means anything. Most guides treat it as a scorecard. It is closer to a debugger for fingerprint spoofing, and reading it that way tells you far more.
The official deployment is abrahamjuliot.github.io/creepjs. The project README, read on 2026-10-22 at github.com/abrahamjuliot/creepjs, warns that other domains using the name are unauthorized mirrors. Its stated purpose is "to shed light on weaknesses and privacy leaks among modern anti-fingerprinting extensions and browsers", and its first two goals are to detect and ignore JavaScript tampering and to fingerprint lie patterns. Keep that in mind: the page is built to catch tools like ours.
Older write-ups describe a "trust score" grade. The current source we read does not render one, so if a guide tells you to aim for a trust percentage, it describes a different version of the page.
FP ID and Fuzzy: what is fingerprinted, and what is ignored
The header shows two hashes.
FP ID is built from the collected sections, with one important twist visible in the source: when a section is flagged as lied about, its values are left out of the hash. If the navigator section lied, navigator data does not go in. If canvas lied, the canvas images do not go in. This is the "detect and ignore tampering" goal in code.
The practical consequence is counterintuitive. Spoofing a value in JavaScript usually does not give you a new FP ID. It removes that value from the ID and adds an entry to the lies list, while everything you did not spoof still identifies you. Two profiles that spoof canvas the same way and share real hardware can end up with the same FP ID.
Fuzzy is a second hash built from a fixed list of metrics (canvas, WebGL, DOMRect, CSS, console errors and more) so that a change in one area changes only part of it. Use it to see which areas moved between two runs.
The lies list
This is the section worth your time. CreepJS walks a long list of API properties and asks whether each one behaves like native browser code. The checks in the source have names like failed toString, failed descriptor, failed own property, failed "prototype" in function, failed class extends error and failed at incompatible proxy error.
Each is a way an override betrays itself. A native getter has no prototype property, throws a specific TypeError when called on the wrong object, and has a property descriptor in a specific shape. A function replaced in JavaScript, or wrapped in a Proxy, tends to get at least one of those details wrong. Each wrong detail is listed against the API it was found on, for example Navigator.hardwareConcurrency.
A lie here does not mean the value is wrong. It means the value was produced by code that is not the browser's own.
Headless, like headless, stealth
Three percentages, each the share of checks in its group that came back true:
- like headless: traits common to headless Chromium, such as no
window.chrome, an empty plugin list, notification permission inconsistencies, a screen with no taskbar, a blankuserAgentData.platform, or aSwiftShaderrenderer in a worker. - headless: direct evidence, such as
navigator.webdriverbeing on orHeadlessChromein the user agent of the page or a worker. - stealth: evidence of stealth patches themselves, such as a tampered
Function.prototype.toString, an oddly placedchromekey onwindow, a fakechrome.runtime, or a WebGL renderer that differs between the page and a worker.
The stealth group is a direct test for the plugin-style patching many automation setups rely on.
Worker scope
CreepJS reads user agent, platform, device memory, core count, language, timezone and GPU again inside a Web Worker and compares them with the main page. Override scripts usually patch the page and forget the worker, so the worker reports the real machine. Mismatches are recorded as lies against WorkerGlobalScope for OS, engine, version and platform version.
Why a clean CreepJS page is not a pass
A result with no lies and 0% stealth tells you your spoofing is not detectable by these particular checks. It says nothing about whether your IP is a datacenter range, whether your timezone matches it, whether your TLS fingerprint matches your claimed browser, or how the account behaves. Commercial risk engines weigh those heavily and keep their models private. CreepJS is also a research project with its own heuristics. A real site may ignore a lie it flags or catch something it does not.
Where CreepJS is the wrong tool
If you are not spoofing anything, for example a stock browser on your own machine, the lies section has nothing to find and the page is mostly an entropy display. For "does this proxy leak", whoer.net or browserleaks answer faster. For "will this site let me in", only that site answers.
Use it as a diff, not a grade
Run CreepJS twice in the same profile, then once after restarting it, and compare the FP ID, the Fuzzy hash and the lies list. A stable identity should produce the same values every time. Then run it on a second profile and confirm the IDs differ. Our consistency checklist lists the other pairs to compare.
Try it on the free tier.
Unlimited local profiles, no credit card. Check it against the detectors yourself.