# Signals

> All 47 components of signalsVersion 9, sorted by the identifier group they belong to, each with its own page.

Source: https://whiskerjs.com/signals/ (last updated 2026-10-26)

> **In plain words:**
>
> A signal (WhiskerJS calls it a component) is one small measurement of the browser and device, such as the screen size, the time zone or how a test image is drawn. One signal alone says little; together they tell browser setups apart. WhiskerJS reads 47 of them, sorts the 37 that rarely change into seven groups, and keeps the 10 that change easily out of the identifiers. It never decides what a signal means: your server does.

## How to read a signal page

Every signal page has the same parts, so you can compare them:

- **Facts box** (at the top): the group, whether the signal is in the visitorId, how it is hashed, whether a browser class excludes it, and the signalsVersion that added it. It is generated from the library’s code.
- **What it reads**: the exact browser API and what is taken from it.
- **How the technique works**: the method, step by step.
- **Why it helps**: what makes it differ between devices.
- **Stability**: what makes it change on the same device.
- **Browser support and what blocks it**: where it is usually `ok`, and what it reports elsewhere.
- **Privacy**: what the value can reveal and how WhiskerJS limits that.
- **How WhiskerJS handles noise and failures**: the statuses and [ruleIds](https://whiskerjs.com/glossary/#ruleid) this component can really report.
- **Example value**: the shape of a real value.

If you would rather read everything on one page, use the [signals reference](https://whiskerjs.com/docs/signals/).

## The groups at a glance

The [structuredId](https://whiskerjs.com/glossary/#structured-id) has one part per [signal group](https://whiskerjs.com/glossary/#signal-group). When two results differ, the group that moved tells you what kind of change happened. Columns: **In visitorId** says whether the component joins both ids; **Always hashed** says whether the value always leaves the library as a [digest](https://whiskerjs.com/glossary/#digest) (other values are hashed only when longer than 128 characters or not printable ASCII).

### gpu: graphics

Graphics output depends on the GPU, its driver, the operating system and the browser’s rendering engine together. It is the most distinguishing group and also the one most likely to move after a driver or browser update.

| Component | Group | In visitorId | Always hashed |
| --- | --- | --- | --- |
| [`canvas`](https://whiskerjs.com/signals/canvas/) | gpu | yes | yes |
| [`webgl_image`](https://whiskerjs.com/signals/webgl-image/) | gpu | yes | yes |
| [`webgl_params`](https://whiskerjs.com/signals/webgl-params/) | gpu | yes | yes |
| [`webgl_renderer`](https://whiskerjs.com/signals/webgl-renderer/) | gpu | yes | no |
| [`webgl_vendor`](https://whiskerjs.com/signals/webgl-vendor/) | gpu | yes | no |
| [`webgpu`](https://whiskerjs.com/signals/webgpu/) | gpu | yes | yes |

### audio

One offline audio render. Nothing is played and no audio device is opened.

| Component | Group | In visitorId | Always hashed |
| --- | --- | --- | --- |
| [`audio`](https://whiskerjs.com/signals/audio/) | audio | yes | yes |

### fonts

Installed fonts and default font settings, which follow the operating system, its language packs and installed software.

| Component | Group | In visitorId | Always hashed |
| --- | --- | --- | --- |
| [`font_preferences`](https://whiskerjs.com/signals/font-preferences/) | fonts | yes | yes |
| [`fonts`](https://whiskerjs.com/signals/fonts/) | fonts | yes | yes (`none` plain) |
| [`fonts_canvas`](https://whiskerjs.com/signals/fonts-canvas/) | fonts | yes | yes (`none` plain) |

### device: screen, memory and CPU

Hardware facts the browser reports: screen size, colour depth, CPU threads and family, memory buckets and touch support.

| Component | Group | In visitorId | Always hashed |
| --- | --- | --- | --- |
| [`color_depth`](https://whiskerjs.com/signals/color-depth/) | device | yes | no |
| [`cpu_architecture`](https://whiskerjs.com/signals/cpu-architecture/) | device | yes | no |
| [`device_memory`](https://whiskerjs.com/signals/device-memory/) | device | yes | no |
| [`hardware_concurrency`](https://whiskerjs.com/signals/hardware-concurrency/) | device | yes | no |
| [`heap_limit`](https://whiskerjs.com/signals/heap-limit/) | device | yes | no |
| [`screen_resolution`](https://whiskerjs.com/signals/screen-resolution/) | device | yes | no |
| [`touch_support`](https://whiskerjs.com/signals/touch-support/) | device | yes | no |

### browser: engine and build

What the browser is and how it was built: its class, its maths and codec support, plugins, Client Hints and browser-specific globals. This group usually moves after a browser update.

| Component | Group | In visitorId | Always hashed |
| --- | --- | --- | --- |
| [`apple_pay`](https://whiskerjs.com/signals/apple-pay/) | browser | yes | no |
| [`browser_class`](https://whiskerjs.com/signals/browser-class/) | browser | yes | no |
| [`math`](https://whiskerjs.com/signals/math/) | browser | yes | yes |
| [`media_codecs`](https://whiskerjs.com/signals/media-codecs/) | browser | yes | yes |
| [`pdf_viewer`](https://whiskerjs.com/signals/pdf-viewer/) | browser | yes | no |
| [`platform`](https://whiskerjs.com/signals/platform/) | browser | yes | no |
| [`plugins`](https://whiskerjs.com/signals/plugins/) | browser | yes | yes (`none` plain) |
| [`private_click`](https://whiskerjs.com/signals/private-click/) | browser | yes | no |
| [`storage`](https://whiskerjs.com/signals/storage/) | browser | yes | no |
| [`ua_client_hints`](https://whiskerjs.com/signals/ua-client-hints/) | browser | yes | no |
| [`vendor`](https://whiskerjs.com/signals/vendor/) | browser | yes | no |
| [`vendor_flavors`](https://whiskerjs.com/signals/vendor-flavors/) | browser | yes | no |

### locale

Language, locale and time zone settings. They move when a user changes them or travels.

| Component | Group | In visitorId | Always hashed |
| --- | --- | --- | --- |
| [`intl_locale`](https://whiskerjs.com/signals/intl-locale/) | locale | yes | no |
| [`languages`](https://whiskerjs.com/signals/languages/) | locale | yes | no |
| [`time_zone`](https://whiskerjs.com/signals/time-zone/) | locale | yes | no |

### prefs: display and accessibility preferences

Five CSS media queries about the screen and the user’s accessibility settings. Each reports the first option that matches.

| Component | Group | In visitorId | Always hashed |
| --- | --- | --- | --- |
| [`color_gamut`](https://whiskerjs.com/signals/color-gamut/) | prefs | yes | no |
| [`dynamic_range`](https://whiskerjs.com/signals/dynamic-range/) | prefs | yes | no |
| [`forced_colors`](https://whiskerjs.com/signals/forced-colors/) | prefs | yes | no |
| [`prefers_contrast`](https://whiskerjs.com/signals/prefers-contrast/) | prefs | yes | no |
| [`reduced_motion`](https://whiskerjs.com/signals/reduced-motion/) | prefs | yes | no |

## Not in either id

These ten are reported so your server can use them, but they change too easily (zoom, taskbar, permission grants, keyboard layout, late-loading voices, browser versions, audio output, content blockers, privacy settings that add noise) to be part of an identifier. Changing them never changes the visitorId or the structuredId.

| Component | Group | In visitorId | Always hashed |
| --- | --- | --- | --- |
| [`audio_latency`](https://whiskerjs.com/signals/audio-latency/) | none | no | no |
| [`canvas_readback`](https://whiskerjs.com/signals/canvas-readback/) | none | no | no |
| [`content_blockers`](https://whiskerjs.com/signals/content-blockers/) | none | no | no |
| [`keyboard_layout`](https://whiskerjs.com/signals/keyboard-layout/) | none | no | no |
| [`noise_profile`](https://whiskerjs.com/signals/noise-profile/) | none | no | no |
| [`permissions`](https://whiskerjs.com/signals/permissions/) | none | no | yes |
| [`pixel_ratio`](https://whiskerjs.com/signals/pixel-ratio/) | none | no | no |
| [`screen_frame`](https://whiskerjs.com/signals/screen-frame/) | none | no | no |
| [`speech_voices`](https://whiskerjs.com/signals/speech-voices/) | none | no | yes (`none` plain) |
| [`ua_versions`](https://whiskerjs.com/signals/ua-versions/) | none | no | no |

## What every signal has in common

- **Always present.** Every one of the 47 components is in every result with exactly one status: `ok`, `unavailable`, `blocked`, `noisy`, `excluded` or `timed_out`. A non-ok status carries a short [ruleId](https://whiskerjs.com/glossary/#ruleid) saying why. See [Statuses and reason codes](https://whiskerjs.com/docs/how-it-works/#statuses-and-reason-codes).
- **Its own clock.** Each component has an inner timeout of 1000 ms, and the whole call has a budget (`timeoutMs`, 5000 ms by default).
- **No prompts, no network, no storage.** No signal asks for a permission, makes a request or writes anything. See [Privacy](https://whiskerjs.com/docs/privacy/).
- **Excluded where a browser randomises it on purpose.** Brave’s [farbling](https://whiskerjs.com/glossary/#farbling), Firefox Fingerprinting Protection, Firefox [resistFingerprinting](https://whiskerjs.com/glossary/#resistfingerprinting) (also Tor Browser and Mullvad Browser) and Safari Advanced Fingerprinting Protection remove some components through the [exclusion table](https://whiskerjs.com/glossary/#exclusion-table), instead of WhiskerJS working around them.
- **Noisy where noise is caught but not listed.** Where a browser or an extension adds noise that no exclusion row covers, the double render or the known-answer probes turn `canvas`, `webgl_image` or `audio` `noisy` (`render_mismatch` or `seeded_noise`), and repeated reads that disagree turn `hardware_concurrency` or `device_memory` `noisy` (`read_varies`). See [Noise handling](https://whiskerjs.com/docs/how-it-works/#noise-handling-the-double-render-and-the-known-answers).

## Left out on purpose

WebRTC (even without addresses), media device lists, network information, battery, geolocation, storage quota and user-agent parsing are not collected, because they need a network-capable API, a permission-coupled API or console output, or because they change too easily. See [the Signals reference](https://whiskerjs.com/docs/signals/#left-out-on-purpose).

## Questions and answers

### How many signals does WhiskerJS read?

47 components at signalsVersion 9. 37 of them are stable and make up the visitorId and the seven parts of the structuredId; the other 10 are reported but kept out of both ids.

### Can I turn a signal off?

Yes. Pass its name in collect({ exclude: [...] }). It then reports status excluded with ruleId caller_excluded, and a stable component counts as missing in both ids, so a different exclude list gives different ids.

### Are the uniqueness or entropy of each signal measured?

Not yet. No per-signal entropy or uniqueness figure for WhiskerJS has been measured, so these pages describe why a signal tends to differ between devices without giving numbers.

## Related pages

- [Signals reference (one page)](https://whiskerjs.com/docs/signals/): Every component on a single page, for reading straight through.
- [Identifiers](https://whiskerjs.com/docs/identifiers/): How the visitorId and structuredId are derived from the stable components.
- [How it works](https://whiskerjs.com/docs/how-it-works/): Statuses, reason codes, hashing and the exclusion table.
- [Glossary](https://whiskerjs.com/glossary/): Short definitions of the terms used on these pages.
- [Browser support matrix](https://whiskerjs.com/browsers/support-matrix/): Which signals each browser family offers, and which WhiskerJS excludes.
