Fingerprint Scan documentation

Fingerprint Scan is an experimental playground for browser fingerprinting and bot detection.

Run a browser, automation framework, anti-detect browser or AI browser agent against it to see the fingerprint it exposes, compare it with other environments, and inspect the signals that contribute to its bot score.

The playground evolves continuously as new fingerprinting and detection ideas are tested. Signals may be added, removed or changed over time.

What Fingerprint Scan collects

Fingerprint Scan collects signals from both the browser and the network.

Browser-side signals include:

  • browser and operating system identity;
  • User-Agent and Client Hints;
  • automation signals such as WebDriver and CDP artifacts;
  • WebGL, WebGPU, GPU and rendering properties;
  • screen and device characteristics;
  • browser APIs and supported features;
  • locale, timezone, media queries and media capabilities;
  • WebRTC and other browser-exposed properties.

Network signals are primarily used for proxy detection. They can also reveal inconsistencies between the network environment and the browser, operating system or device identity claimed by the client.

The playground intentionally exposes many of the collected signals. The goal is to let you inspect what your browser exposes rather than only returning a final score.

Interpreting the signals

A useful way to read the results is to distinguish between claimed identity and observed properties.

The User-Agent and Client Hints describe what the browser claims to be: for example, a specific version of Chrome running on Windows. Fingerprint Scan then collects properties from different browser APIs and execution contexts to check whether that identity holds up.

For example:

  • does the WebGL renderer make sense for the claimed operating system?
  • are browser APIs present or absent as expected for the claimed browser and version?
  • do navigator.platform, screen properties and other device signals agree with each other?
  • do values collected from different JavaScript execution contexts remain consistent?
  • do HTTP headers and Client Hints agree with the browser identity exposed to JavaScript?

A mismatch does not automatically mean that the browser is automated. Privacy extensions, unusual hardware, browser modifications and anti-fingerprinting features can all create legitimate inconsistencies. What matters is the type of signal and the combination of evidence.

Strong signals

Some signals are direct or strongly correlated indicators of browser automation. For example, navigator.webdriver === true is strongly associated with a browser controlled through WebDriver. Some Chrome DevTools Protocol side effects and artifacts introduced by automation frameworks can provide similarly strong evidence.

A strong signal can have a significant effect on the bot score without requiring many additional anomalies.

Inconsistency checks

Other tests do not detect automation directly. Instead, they look for contradictions between properties that would normally be correlated.

{
  "claimedIdentity": {
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ... Chrome/148 Safari/537.36",
    "clientHintsPlatform": "Windows"
  },
  "observedProperties": {
    "webglRenderer": "ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Max, Unspecified Version)",
    "platform": "MacIntel"
  }
}

Neither property is necessarily suspicious on its own. The interesting signal is that they do not agree.

The same principle can be applied across many browser properties. A browser may claim one identity through its User-Agent while exposing a different environment through WebGL, browser APIs, platform information, HTTP headers or other signals.

Fingerprint Scan can also compare properties collected from different JavaScript execution contexts, such as the main page, iframes and workers. Differences between contexts can indicate that some browser properties have been modified or spoofed incompletely.

This is particularly useful when testing anti-detect browsers or automation setups that modify parts of the browser fingerprint.

Weak signals

Some properties are only weak indicators. An unusual screen size, timezone, GPU, number of CPU cores or network characteristic is not proof of automation.

These signals become more interesting when several independent properties point in the same direction or contradict the identity presented by the browser.

When interpreting a scan, a useful order is:

  1. look for direct automation artifacts;
  2. look for inconsistencies between related properties;
  3. consider combinations of weaker anomalies.

Comparing fingerprints

Fingerprint Scan generates multiple fingerprint parts instead of only producing one hash for the entire browser.

The current fingerprint includes parts covering areas such as GPU, screen, locale and timezone, device information, browser features, media queries, media capabilities, WebAssembly and WebRTC.

{
  "gpu": "c8e02225e262aedb",
  "screen": "e37af652bc8d5518",
  "localeTimezone": "fcb2e510b7c1dd19",
  "deviceInfo": "84a4689f5e0708d8",
  "browserFeatures": "bea7985567d45a6f",
  "mediaQueries": "77a774ed88af7ac9",
  "media": "515fc65b3b250eb3",
  "webAssembly": "def27b6e78ab7558",
  "webRtc": "b51e1e20baa95325"
}

This makes it easier to compare two runs and understand what changed. For example, if you modify the graphics environment of an automated browser, you may see the GPU fingerprint change while the screen, locale and device fingerprints remain identical.

The fingerprint parts are not simple hashes of the raw signals. Values are normalized and processed before hashing so that some minor changes do not necessarily produce a completely different fingerprint part.

Fingerprint hash

fingerprintHash is derived from the fingerprint parts. It can be treated as a compact summary of the overall device fingerprint. If one or more meaningful fingerprint parts change, the overall fingerprint hash may change as well.

Fingerprint ID

fingerprintId serves a different purpose. It is a tracking identifier intended to recognize the same browser across visits, independently from changes to some fingerprint properties.

The current implementation is intentionally simple and relies mostly on browser storage mechanisms such as cookies, local storage and session storage. This part of the playground is experimental and may evolve in the future with additional identification techniques.

Bot score

The bot score estimates how suspicious the current browser and network environment looks based on the signals available to Fingerprint Scan.

It combines two main sources of information:

  • browser fingerprinting and automation detection;
  • network and proxy detection.

Not every signal has the same weight. Strong automation indicators can be enough to produce a high score. For example, an exposed navigator.webdriver value or a known CDP automation artifact provides much stronger evidence than an unusual screen resolution.

When no strong indicator is present, the score is based on combinations of weaker signals and inconsistencies. This is why a human using an unusual browser, privacy extensions, anti-fingerprinting protections or other browser modifications may receive a score above zero.

Network and proxy signals contribute to the same score. They are primarily used to identify characteristics associated with proxies, but can also be useful for finding inconsistencies between the network environment and the device identity exposed by the browser.

No behavioral scoring

The bot score does not use behavioral or traffic-volume detection.

How often you visit Fingerprint Scan, how quickly you interact with the page, how many scans you run, or how many requests you previously made does not increase your score.

The score is based on the browser fingerprint, automation signals and network environment observed during the scan. This makes it possible to run the playground repeatedly while modifying a browser configuration and compare the results without previous runs influencing the next score.

Fingerprint Scan is a playground, not a production bot verdict. A score should be interpreted together with the signals that produced it.

Programmatic access

Once collection has completed, the result is exposed through the stable public interface: window.FINGERPRINT_SCAN.

This allows scripts, browser automation and AI agents to inspect the scan directly without parsing the visual interface or relying on screenshots.

console.log(window.FINGERPRINT_SCAN);
JSON.stringify(window.FINGERPRINT_SCAN, null, 2);

The object exposes the public result of the scan, including:

  • collection status;
  • fingerprint hash;
  • fingerprint ID;
  • fingerprint parts;
  • bot score;
  • visible fingerprint and detection signals.
{
  "status": "COLLECT_SUCCESSFUL",
  "fingerprintHash": "...",
  "fingerprintId": "...",
  "fingerprintParts": { ... },
  "botScore": 42,
  "signals": {
    "general": {
      "userAgent": "...",
      "platform": "..."
    },
    "gpu": {
      "webgl": { ... },
      "webGPU": { ... }
    }
  }
}

Private detection signals and internal Castle scores are deliberately not exposed.

window.FINGERPRINT_SCAN is intended to be a stable interface for programmatic use. Fingerprint Scan itself is experimental, so individual signals may be added, removed or changed as new detection ideas are tested. The public object structure is designed to remain stable or backwards compatible as the playground evolves.

Using the output with AI agents

The programmatic interface is also intended to make Fingerprint Scan easy to use from AI coding tools and browser agents.

An agent can open the playground, wait for collection to complete, read window.FINGERPRINT_SCAN, and inspect the resulting fingerprint and detection signals directly.

When interpreting the result, agents should use the same hierarchy described above:

  1. identify strong automation artifacts;
  2. look for inconsistencies between the claimed browser identity and observed properties;
  3. compare related signals and fingerprint parts;
  4. treat isolated weak anomalies cautiously;
  5. use the bot score as a summary of the evidence, not as the explanation itself.

This makes it possible to use Fingerprint Scan as a debugging step when experimenting with browser automation or different browser environments.

Playwright

Fingerprint Scan can be inspected directly from a Playwright-controlled browser.

The script should:

  1. navigate to https://fingerprint-scan.com;
  2. wait until window.FINGERPRINT_SCAN.status indicates that collection succeeded;
  3. retrieve window.FINGERPRINT_SCAN;
  4. print or process the resulting object.
import { chromium } from "playwright";

const browser = await chromium.launch();
const page = await browser.newPage();

await page.goto("https://fingerprint-scan.com");

await page.waitForFunction(
  () => window.FINGERPRINT_SCAN?.status === "COLLECT_SUCCESSFUL"
);

const result = await page.evaluate(
  () => window.FINGERPRINT_SCAN
);

console.log(JSON.stringify(result, null, 2));

await browser.close();

Puppeteer

The same programmatic interface can be used from Puppeteer.

import puppeteer from "puppeteer";

const browser = await puppeteer.launch();
const page = await browser.newPage();

await page.goto("https://fingerprint-scan.com");

await page.waitForFunction(
  () => window.FINGERPRINT_SCAN?.status === "COLLECT_SUCCESSFUL"
);

const result = await page.evaluate(
  () => window.FINGERPRINT_SCAN
);

console.log(JSON.stringify(result, null, 2));

await browser.close();

Other browser automation frameworks can use the same approach as long as they can execute JavaScript in the page.

Example automation result

The following is a shortened result captured from a real headless Chromium automation run against Fingerprint Scan.

Values will differ depending on the browser, machine, operating system, network, proxy and automation configuration.

Shortened window.FINGERPRINT_SCAN result
{
  "status": "COLLECT_SUCCESSFUL",
  "fingerprintHash": "af86241101ad674d",
  "fingerprintId": "5729707b85b2496c9171a5e22e7acc84",
  "fingerprintParts": {
    "gpu": "c8e02225e262aedb",
    "screen": "e37af652bc8d5518",
    "localeTimezone": "fcb2e510b7c1dd19",
    "deviceInfo": "84a4689f5e0708d8",
    "browserFeatures": "bea7985567d45a6f",
    "mediaQueries": "77a774ed88af7ac9",
    "media": "515fc65b3b250eb3",
    "webAssembly": "def27b6e78ab7558",
    "webRtc": "b51e1e20baa95325"
  },
  "botScore": 100,
  "signals": {
    "general": {
      "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/151.0.7922.34 Safari/537.36",
      "platform": "MacIntel",
      "hardwareConcurrency": 14,
      "deviceMemory": 32
    },
    "browser": {
      "plugins": {
        "items": [],
        "count": 0
      },
      "browserFeatures": {
        "chrome": false,
        "brave": false,
        "serial": true,
        "webAssembly": true,
        "usb": true,
        "browserCapture": true,
        "battery": true,
        "bitmask": "00001111000101111001111111111"
      }
    },
    "locale": {
      "language": "en-US",
      "languages": ["en-US"],
      "timezone": "Europe/Paris"
    },
    "display": {
      "screen": {
        "width": 1280,
        "height": 720,
        "availWidth": 1280,
        "availHeight": 720,
        "colorDepth": 24,
        "devicePixelRatio": 1,
        "innerWidth": 1280,
        "innerHeight": 720
      }
    },
    "gpu": {
      "webgl": {
        "vendor": "Google Inc. (Google)",
        "renderer": "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (LLVM 10.0.0) (0x0000C0DE)), SwiftShader driver)"
      },
      "webGPU": {
        "available": false
      }
    },
    "network": {
      "network": {
        "effectiveType": "4g",
        "downlink": 9.8,
        "rtt": 0,
        "saveData": false
      },
      "networkProbeFetch": {
        "ok": true,
        "status": 200,
        "roundTripMs": 792.6,
        "timing": {
          "dnsMs": 0.5,
          "tcpMs": 0.4,
          "tlsMs": 0,
          "nextHopProtocol": "http/1.1"
        }
      }
    },
    "botSignals": {
      "webdriver": true,
      "cdpStackCheck": true,
      "playwrightFlags": false,
      "chromeDriverFlags": false
    }
  }
}

This example is intended to show the structure of a real result. The live output from window.FINGERPRINT_SCAN should be used when analyzing a specific browser.

Research and experiments

Fingerprint Scan is intentionally experimental.

The playground is a place to test browser fingerprinting and bot detection ideas against real browsers, automation frameworks, anti-detect browsers and unusual browser environments.

New signals can be tested in the playground before deciding whether they are useful detection techniques. This includes direct automation artifacts, browser inconsistencies, fingerprinting techniques and network signals.

If you find an interesting inconsistency, have a signal you would like to test, or notice a false positive, ping me on X.