CreepJS Test Guide: How to Read a Fingerprint Consistency Report (2026)

8 min read

Most fingerprint test pages print what your browser reports and stop there. Canvas hash, WebGL renderer, a list of fonts. Useful, but it can't tell you whether those values make sense together.

CreepJS goes a step further. It asks the same question from several places and notes where the answers disagree. That makes it a good tool for QA work, for privacy research, and for anyone running separate profiles in a multi-account browser who wants to confirm each profile is internally coherent.

This walkthrough covers how to read the report. It's an explanation of what the page shows, not a promise about how any website will treat a browser. Treat the output as a debugging aid.

What We're Building

By the end you'll be able to:

  1. Find and read the main sections of a CreepJS report
  2. Tell a real contradiction from noise (there's more noise than you'd expect)
  3. Check that a profile gives the same values from run to run
  4. Check that two profiles differ from each other where they should
  5. Know what a consistency check cannot tell you

Prerequisites

  • A browser profile to test, from JustBrowser or any other tool
  • A basic grasp of fingerprinting concepts (canvas, WebGL, audio context)
  • 15 minutes
  • The CreepJS test page

For background on how GPU-based fingerprinting works, the WebGL fingerprinting deep-dive is a good companion. Not required.

Step 1: Run the Test

Open the profile and navigate to the CreepJS page. Let it finish. It takes a few seconds, and scrolling or clicking around while it runs isn't a good idea.

When it's done you'll see a summary near the top and a long list of expandable sections underneath. Intimidating. Most of it you can ignore on a first pass.

One caution about the percentages near the top. Each one summarises many checks, and what matters is which checks are flagged. A middling score with a clear, explainable cause tells you more than a high score you haven't looked into.

Step 2: The Lies Section

Scroll to the section labelled "Lies". This is the most informative part of the report.

CreepJS queries the same data from more than one context and compares:

  • Main thread vs Web Worker. A browser should report the same values whether the page or a Web Worker asks. If a tool changes values only in the page's own JavaScript, a Worker can still return the originals, and the two disagree.
  • Prototype chain integrity. If a property such as navigator.webdriver has been redefined with a getter, CreepJS looks at whether its descriptor looks like a built-in one. Redefined properties often carry different flags.

Anything listed here is a contradiction in the data, so read each entry. A few you may run into:

Worker mismatch. The page and a Worker report different navigator values. This points at changes applied at the page level that didn't reach Workers.

Prototype lies. Something altered a built-in's prototype in a way CreepJS can see. There's rarely a setting to fix it, because it comes from how a tool applies its changes.

Headless signals. If you launched the profile through the API, remember that POST /profiles/{id}/start starts headless unless the body says {"headless": false}. A headless session has no real window, so its screen and viewport follow headless defaults. Start the profile headed when you're checking what a person would see.

Step 3: The Fingerprint Section

Below Lies, the Fingerprint section shows values and hashes for the main vectors:

  • Canvas 2D: a hash of how the browser renders a test image
  • WebGL: the GPU renderer string plus a hash of rendered 3D content
  • Audio: how the browser processes an AudioContext oscillator
  • Fonts: which fonts the system reports
  • Screen: resolution, colour depth, pixel ratio

Two checks are worth doing here.

Stability. Run the test twice on the same profile. The hashes should match. If a canvas hash changes between runs, the value is being randomised when it should be fixed for that profile. A profile that looks different on every visit is a poor test subject, and a poor basis for any workflow that expects a returning browser.

Separation. Run it on two different profiles. Each profile is its own browser with its own cookies, storage and proxy, and its identity parameters should differ accordingly. Identical hashes on two profiles you meant to keep apart are worth a look.

Also read the WebGL renderer string with a critical eye. A renderer that names SwiftShader is a software renderer, which usually means a headless or virtualised setup. A string that names a real GPU is what you'd expect on a desktop machine.

Fonts are a good consistency test too. A Windows identity should carry a Windows font set and a macOS identity a Mac one. A list mixing Helvetica Neue with Segoe UI is unlikely on a real system.

Step 4: Putting the Pieces Together

The report is easiest to read as three questions:

  • Stable? Do repeated runs return the same values?
  • Coherent? Do the reported platform, fonts, graphics and screen fit together?
  • Native-looking? Do the built-in functions look like built-ins, in every context?

If one area is flagged and the others are clean, start there. The shape of the failure usually points at the cause: a Worker mismatch suggests changes made at page level, while an unstable canvas hash suggests randomisation.

For Chrome profiles, JustBrowser takes the engine route: they run its own custom Chromium engine with native C++ integration, patched on every upstream Chromium release, with 40+ identity parameters covering canvas, WebGL, audio, fonts, screen, timezone, navigator and hardware. That's an architectural choice, and it's why it isn't an extension. We're not going to quote a CreepJS score for it here. Scores are a snapshot of one version of a third-party page, and you should run the test on your own profiles and read what comes back.

Step 5: Common Findings

Worker values don't match the page

Likely cause: values changed in the page's JavaScript only, and Workers still see the originals.

Next step: this is architectural, not a configuration problem. Check how the tool you're using applies its changes.

Values change between runs

Likely cause: a value is randomised per session instead of held per profile.

Next step: look for a per-profile stability setting. Values should be deterministic for a profile.

Headless signals

Likely cause: the profile was started headless, which is the API default.

Next step: start it with {"headless": false} when you need a headed session. If you attach over CDP, use the existing context (contexts[0]) rather than creating a new one, which would sit outside the profile.

A clean report isn't the whole picture

Likely cause: CreepJS is one view of the browser layer. It doesn't test your proxy or network connection.

Next step: a consistency report is a check on one layer. Verify the proxy separately, and keep in mind that a clean report is not a guarantee about how any service will respond.

Next Steps

  1. Try a second checker. Different tools measure different things, so compare what each reports.
  2. Recheck after changes. A new proxy, a changed timezone or an edited identity parameter can change the picture.
  3. Note the settings. Once a profile behaves the way you want, record its settings so you can see what changed later.

If you're testing your own site across geos and devices, or checking ads you run, a stable, coherent profile is simply a more reliable test fixture. That's the practical value of the exercise. Run the test, read the Lies section first, and fix what it lists.

Frequently Asked Questions

What does CreepJS actually measure?

CreepJS collects a browser's fingerprint values and cross-checks them against each other. It looks at whether the main thread and a Web Worker agree, whether built-in functions look native, and whether the reported platform, fonts and graphics fit together. It reports inconsistencies; it does not tell you what any particular website will do.

Why would a browser fail the CreepJS Lies section?

CreepJS compares main-thread values against Web Worker values, inspects prototype chains for tampering and looks at property descriptors. If a tool changes values in one place but not another, the mismatch shows up.

Which CreepJS sections are most useful for a multi-account browser?

Start with Lies, then the fingerprint hashes (canvas, WebGL, audio, fonts, screen). The Lies section lists contradictions in the data. The hashes show whether a profile's values stay stable between runs and differ between profiles.

How often should I run a fingerprint check on my profiles?

Run it when you create a profile template and after you change fingerprint or proxy settings. Rerun it if something starts behaving differently. The checker itself changes over time, so a past reading is only a snapshot.


Try JustBrowser

A multi-account browser for teams, built on a custom Chromium engine with native C++ integration, not an extension. Real C++ engine patches at the canvas / WebGL / audio / font / screen layer, with 40+ identity parameters per profile. REST API plus CDP for Playwright, Puppeteer and Selenium. Available for Windows x64 and macOS (Apple Silicon). One plan: $9.99/month (or $99.99/year) with unlimited profiles. 7-day free trial, card required — cancel before the trial ends and you are not charged. Unlimited cloud sync. Team seats are free — invite as many teammates as you like.

Get started → · See how it compares

Tags
  • creepjs-test
  • multi-account-browser
  • fingerprint-testing
  • profile-consistency
  • browser-fingerprinting
  • buildinpublic
  • saasstudio
  • aiworkforce
  • buildwithclaude

Ready to manage multiple accounts?

Seven days free, then $9.99/month — one plan, everything included.

We'd like to use Google Analytics, a Google service, to understand how our website is used. It sets two cookies in your browser and runs only if you click Accept. You can change your choice at any time with Cookie settings. Cookie Policy

Sign-in cookies and the cookie that remembers this choice are always on; the website needs them to work.

Google Analytics, a Google service, helps us understand how our website is used. It sets two cookies, _ga and _ga_TVZHQ99TZW. It is now onoff in this browser. If your browser sends a Global Privacy Control or Do Not Track signal, it stays off. Cookie Policy