Browsers · WhiskerJS 0.1.0

Chrome (desktop)

Desktop Chrome offers every API WhiskerJS reads, and the library excludes nothing there.

In plain words

Desktop Chrome is the browser where WhiskerJS can read the most: it offers every API the library uses, including a few that only Chromium-based browsers have, such as the device memory and Client Hints. WhiskerJS removes nothing on purpose here. The main things to know are that one small audio signal is skipped to keep the console clean, and that automated or headless Chrome tends to raise integrity flags.

At a glance

  • Browser class: chromium (the user agent has a Chrome/<number> token).
  • Excluded components: none.
  • Chromium-only components that are ok here: device_memory, heap_limit, ua_client_hints, ua_versions, keyboard_layout.
  • Skipped on purpose: audio_latency.
  • Lab sample: Mac Chrome kept one id across 4 runs at signalsVersion 1.

What works

Nearly everything. The graphics group reads a 2D canvas scene twice, a WebGL triangle twice, 21 WebGL parameters with shader precisions and extensions, and the unmasked renderer and vendor through WEBGL_debug_renderer_info. Where Chrome has WebGPU enabled, webgpu hashes the adapter’s info, features and seven limits; whether navigator.gpu exists depends on the Chrome version, the operating system and the GPU, so treat it as variable.

The Chromium-only components work here:

  • device_memory from navigator.deviceMemory (coarse buckets such as 8; from Chromium 146 with the tag ;c146, for example 16;c146);
  • heap_limit from performance.memory.jsHeapSizeLimit;
  • ua_client_hints: brands without versions (GREASE removed), mobile, platform, and from one getHighEntropyValues() call the architecture, bitness, wow64, model and form factors;
  • ua_versions: full brand versions and the platform version, kept out of both ids because they change with every update;
  • keyboard_layout from navigator.keyboard.getLayoutMap(), outside the ids.

Fonts, font preferences, audio, Math, codecs, locale, the five display preference queries, storage presence and permission states are all read as on any current browser.

What is unavailable or blocked

  • audio_latency is unavailable with ruleId skipped. Desktop Chrome prints an autoplay warning when a page creates an AudioContext without a user gesture, and WhiskerJS writes nothing to the console.
  • apple_pay and private_click report the plain value none: these are Safari features.
  • ua_client_hints and ua_versions end with he=blocked instead of the detailed hints when a Permissions Policy refuses them, for example in an embedded frame. That marker is stable, so it does not make the id jump.
  • keyboard_layout is blocked in cross-origin iframes, where Chrome refuses the call.
  • webgpu is unavailable with no_api where navigator.gpu is missing, or no_context when no adapter is returned (seen in a headless container run).

What WhiskerJS excludes and why

Nothing. The exclusion table has an empty row for the chromium class, because stock Chrome does not randomise the signals WhiskerJS reads. If an extension adds noise to canvas or WebGL readback, the double render catches it and the component becomes noisy with ruleId render_mismatch, and canvas_readback reports altered. The known-answer probes also catch noise that is the same on both reads (noisy / seeded_noise, for canvas, webgl_image and audio).

Integrity flags to expect

From the integrity checks and their documented false positives:

  • automation_webdriver: Selenium, Playwright and Puppeteer set navigator.webdriver by default, and so do your own end-to-end tests.
  • headless_user_agent: the user agent contains HeadlessChrome.
  • headless_markers: an outer window of 0x0, or a desktop Chrome user agent without window.chrome. Some embedded Chromium views raise it too.
  • software_renderer: the WebGL renderer names SwiftShader or similar. Real users with hardware acceleration switched off, or on remote desktops, raise it as well.
  • native_tampering, prototype_lie and navigator_own_property: expect them from privacy and anti-fingerprinting extensions.
  • permissions_inconsistent: notifications denied while the permissions query says prompt, a known headless Chrome trait.
  • ua_ch_mismatch: the user agent disagrees with Client Hints (Chromium version, platform, mobile), or a brand contains Headless. User-agent switchers raise it too.
  • exposed_binding: Playwright exposed a function to the page.
  • vm_renderer: the WebGL renderer names a virtual machine adapter (VMware, VirtualBox, Parallels, QEMU and KVM, Hyper-V). Real users on virtual desktops raise it too.

For illustration only: a headless Chromium run in a test container (not the lab sample) was classed other at signalsVersion 5 (it is chromium since signalsVersion 9), had a SwiftShader WebGL renderer and raised automation_webdriver, headless_user_agent, permissions_inconsistent and software_renderer. Any single flag is weak evidence; your server weighs them.

Early lab observations

An early, small sample at signalsVersion 1, not a benchmark: on a Mac, Chrome kept one id across 4 runs, and that id differed from the ids of the other three device and browser pairs in the sample. device_memory was ok. The sample used the first collector (30 components), not the current 47, and involved no other library.

Testing it yourself

Open the live demo in Chrome, run it a few times, and come back after a browser update to see which structuredId group moves. The lab mode copies one tab-separated line per run, so you can collect runs from several machines and compare them. Nothing is sent anywhere.

Support table for Chrome (desktop)

Expected status of every component in this browser at signalsVersion 9, from the support matrix (updated 2026-10-26). Basis "code" means expected from the library code and documentation, not yet measured; "lab" means seen in the early lab sample.

ComponentStatusBasisNote
apple_paynonecodeNo ApplePaySession: reports the plain value none.
audiookcodenoisy / seeded_noise if the audio known-answer probe sees noise.
audio_latencyunavailablecodeSkipped (unavailable / skipped): creating an AudioContext here would print a console warning.
browser_classokcodeClass chromium.
canvasokcodenoisy / render_mismatch or seeded_noise if an extension or setting adds noise to readback.
canvas_readbackokcodeexact, or altered when an extension adds noise.
color_depthokcode
color_gamutokcode
content_blockersokcodenone, or the bait names a content blocker hid.
cpu_architectureokcode
device_memoryoklabSeen ok on Mac Chrome in the lab sample. From Chromium 146 the value carries the tag ;c146 (new buckets).
dynamic_rangeokcode
font_preferencesokcode
fontsokcode
fonts_canvasokcode
forced_colorsokcode
hardware_concurrencyokcodeRead 16 times; noisy / read_varies if the reads disagree. Some browsers cap or round the value.
heap_limitokcode
intl_localeokcode
keyboard_layoutokcodenavigator.keyboard.getLayoutMap() is a Chromium desktop API; blocked in cross-origin iframes.
languagesokcode
mathokcode
media_codecsokcode
noise_profileokcodecanvas=exact,webgl=exact,audio=exact on a stock browser; webgl=na without WebGL.
pdf_viewerokcode
permissionsokcode
pixel_ratiookcode
platformokcode
pluginsokcode
prefers_contrastokcode
private_clicknonecodeNo attributionSourceId on anchors: reports the plain value none.
reduced_motionokcode
screen_frameokcode
screen_resolutionokcode
speech_voicesokcodeDigest of the voice list, or none; some browsers fill the list late.
storageokcode
time_zoneokcode
touch_supportokcode
ua_client_hintsokcodehe=blocked when a Permissions Policy refuses the detailed hints.
ua_versionsokcode
vendorokcode
vendor_flavorsokcode
webgl_imageokcodenoisy / render_mismatch or seeded_noise if an extension or setting adds noise to readback.
webgl_paramsokcodeWhere WebGL 1 is available; unavailable / no_context without it.
webgl_rendererokcode
webgl_vendorokcode
webgpuvariescodeDepends on browser version, operating system and GPU; needs a secure context. unavailable / no_api without navigator.gpu, no_context without an adapter.

Questions and answers

Is anything excluded in Chrome?

No. The exclusion table has no row for the chromium class, so all 47 components are collected. Some can still be noisy if an extension adds noise to canvas, WebGL or audio readback.

Why is audio_latency unavailable in Chrome?

Creating an AudioContext outside a user gesture makes desktop Chrome print an autoplay warning in the console, and WhiskerJS never writes to the console. So it reports unavailable with ruleId skipped. Nothing is wrong with the browser.

What browser_class does headless Chrome show?

chromium, since signalsVersion 9. A headless Chromium whose user agent names only HeadlessChrome/ was classed other until signalsVersion 8, because the class test looked for a Chrome/ token and the old headless mode lacks window.chrome. The headless flags, such as headless_user_agent, report the rest.

Why does device_memory end in ;c146?

Chromium 146 changed the memory buckets (up to 32 GB on desktop, where the old cap was 8). From that version WhiskerJS tags the value, for example 16;c146, so a server can tell which scheme it comes from. Expect one id change per user at the update to 146.