# Browsers

> What WhiskerJS reads in each browser family, what the browser blocks, and what the library leaves out on purpose.

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

> **In plain words:**
>
> Browsers do not all offer the same features, and some privacy-focused browsers deliberately blur certain measurements. WhiskerJS works in all of them: it always returns all 47 components, each with a status that says whether it was read, missing, or left out on purpose. The pages below explain, for each browser family, what WhiskerJS can read, what the browser blocks, and what the library skips so that your ids do not fill with noise.

## Browser classes

Before it collects anything, WhiskerJS sorts the browser into one of ten classes, using cheap hints that never prompt: `navigator.brave`, the user agent, `navigator.vendor`, the `userAgentData` brands, for Firefox a set of resistFingerprinting traits, and two tiny [known-answer probes](https://whiskerjs.com/signals/noise-profile/) (a canvas and a WebGL read whose exact answer is known in advance). The checks run in this order, and the first match wins:

1. **`brave`**: `navigator.brave.isBrave` is a function.
2. **Firefox**: the user agent has a `Firefox/<number>` token, or the window has `mozInnerScreenX`. It is `firefox_rfp` only when every resistFingerprinting trait holds at once, and `rfp_hardened` (Tor Browser, Mullvad Browser) when, on top, the window sits on the letterboxing steps and the locale is the spoofed English (see [Firefox resistFingerprinting, Tor and Mullvad](https://whiskerjs.com/browsers/firefox-rfp-and-tor/)). Otherwise it is `firefox_fpp` (Fingerprinting Protection) when a known-answer probe came back altered, else `firefox`.
3. **Chromium**: the user agent has a `Chrome/<number>` or `Chromium/<number>` token, or names only `HeadlessChrome/`, or `window.chrome` exists and the vendor is `Google Inc.`. It is `chromium_webview` for Android WebView (the `wv` token or the `Android WebView` brand), else `chromium`.
4. **Safari**: the vendor is `Apple Computer, Inc.`, or the user agent has both `Safari/` and `Version/`. It is `safari_afp` (Advanced Fingerprinting Protection) when a known-answer probe came back altered, else `safari`.
5. **`other`**: anything else.

The class does two things. It is reported as the [`browser_class`](https://whiskerjs.com/signals/browser-class/) component (part of the visitorId), and it picks the row of the [exclusion table](https://whiskerjs.com/docs/how-it-works/#the-exclusion-table). It is a guess from values the client controls, so your server should treat it as a hint.

## The nine browser families

| Family | Class | Excluded components | Page |
| --- | --- | --- | --- |
| Chrome (desktop) | `chromium` | none | [Chrome desktop](https://whiskerjs.com/browsers/chrome-desktop/) |
| Chrome on Android | `chromium`; `chromium_webview` in Android WebView | none | [Chrome Android](https://whiskerjs.com/browsers/chrome-android/) |
| Safari on macOS | `safari`; `safari_afp` with Advanced Fingerprinting Protection | none; 5 under `safari_afp` | [Safari macOS](https://whiskerjs.com/browsers/safari-macos/) |
| Safari and every iOS browser | `safari`; `safari_afp` with Advanced Fingerprinting Protection | none; 5 under `safari_afp` | [Safari and iOS browsers](https://whiskerjs.com/browsers/safari-ios/) |
| Firefox | `firefox`; `firefox_fpp` with Fingerprinting Protection | none; 3 under `firefox_fpp` | [Firefox](https://whiskerjs.com/browsers/firefox/) |
| Firefox resistFingerprinting, Tor Browser, Mullvad Browser | `firefox_rfp`, `rfp_hardened` | 9 | [Firefox RFP, Tor, Mullvad](https://whiskerjs.com/browsers/firefox-rfp-and-tor/) |
| Brave | `brave` | 12 | [Brave](https://whiskerjs.com/browsers/brave/) |
| Microsoft Edge | `chromium` | none | [Edge](https://whiskerjs.com/browsers/edge/) |
| Samsung Internet | `chromium` | none | [Samsung Internet](https://whiskerjs.com/browsers/samsung-internet/) |

- [Chrome (desktop)](https://whiskerjs.com/browsers/chrome-desktop/): Every Chromium API, nothing excluded.
- [Chrome on Android](https://whiskerjs.com/browsers/chrome-android/): Client Hints with the device model, audio latency read.
- [Safari on macOS](https://whiskerjs.com/browsers/safari-macos/): Apple Pay and Private Click Measurement, no Chromium APIs.
- [Safari and every iOS browser](https://whiskerjs.com/browsers/safari-ios/): Why Chrome on iPhone is classed safari.
- [Firefox](https://whiskerjs.com/browsers/firefox/): A stable canvas id, and Fingerprinting Protection.
- [Firefox resistFingerprinting, Tor and Mullvad](https://whiskerjs.com/browsers/firefox-rfp-and-tor/): The exact test and the nine excluded components.
- [Brave](https://whiskerjs.com/browsers/brave/): Farbling, and the twelve components left out.
- [Microsoft Edge](https://whiskerjs.com/browsers/edge/): A Chromium browser like Chrome.
- [Samsung Internet](https://whiskerjs.com/browsers/samsung-internet/): Chromium on Android, with its own global.

## Four kinds of “missing”

A component that is not `ok` always says why, with a status and a ruleId. Across browsers you will mostly meet four cases:

- **The API does not exist here**: `unavailable` with ruleId `no_api`. Example: `device_memory` in Safari and Firefox, because only Chromium-based browsers offer `navigator.deviceMemory`.
- **WhiskerJS skips it here on purpose**: `unavailable` with ruleId `skipped`. Only [`audio_latency`](https://whiskerjs.com/signals/audio-latency/) does this, outside Safari and Android, to avoid a console warning.
- **The browser blurs it on purpose**: `excluded` with ruleId `brave_farbling`, `firefox_fpp`, `firefox_rfp`, `rfp_hardened` or `safari_afp`, from the exclusion table.
- **The browser or an extension adds noise**: `noisy` with ruleId `render_mismatch`, when the [double render](https://whiskerjs.com/glossary/#double-render) of `canvas` or `webgl_image` differs; `seeded_noise`, when the renders agree but a [known-answer probe](https://whiskerjs.com/signals/noise-profile/) of the same API (canvas, WebGL or audio) came back wrong; or `read_varies`, when 16 reads of `hardware_concurrency` or `device_memory` disagree.

Every non-ok status counts the same in the visitorId (as “missing”), so a component that is `blocked` in one run and `timed_out` in the next does not change the id. See [Statuses and reason codes](https://whiskerjs.com/docs/how-it-works/#statuses-and-reason-codes).

## How much is measured

Very little so far, and this section says so plainly. The [support matrix](https://whiskerjs.com/browsers/support-matrix/) is filled from the library code and documentation. The only real observations are an early, small sample at signalsVersion 1, not a benchmark: Mac Chrome (4 runs), Mac Safari (2 runs), iPhone Chrome (3 runs) and iPhone Safari (3 runs) each kept one id across their runs, the four ids differed from each other, and `device_memory` was unavailable on Safari and iOS. No head-to-head measurement against other libraries has run.

You can measure your own devices with the lab mode of the [live demo](https://whiskerjs.com/demo/#lab).

## Questions and answers

### Does WhiskerJS work in every browser?

collect() runs and resolves in every browser, because each component reports a status instead of failing. What differs is which components are ok: some APIs exist only in Chromium-based browsers, and some components are excluded on purpose in Brave, in Firefox with Fingerprinting Protection or resistFingerprinting, in Tor and Mullvad Browser, and in Safari with Advanced Fingerprinting Protection.

### Why is Chrome on iPhone treated like Safari?

On iOS every browser uses Apple's WebKit engine. Chrome, Firefox and Edge on iOS report the Apple vendor string and no Chrome/ token in the user agent, so WhiskerJS classes them as safari.

### Is the browser support table measured?

Mostly not yet. It is filled from the library code and documentation, plus one early lab sample of four device and browser pairs at signalsVersion 1. It will be refreshed from aggregated diagnostics later.

## Related pages

- [Browser support matrix](https://whiskerjs.com/browsers/support-matrix/): All 47 components in nine browser families, in one table.
- [The exclusion table](https://whiskerjs.com/docs/how-it-works/#the-exclusion-table): Which components are left out in each privacy class.
- [browser_class](https://whiskerjs.com/signals/browser-class/): The component that reports the class.
- [Live demo](https://whiskerjs.com/demo/): See what your own browser reports.
