# Brave

> Brave randomises some signals per site and per session; WhiskerJS recognises Brave and does not collect those twelve components at all.

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

> **In plain words:**
>
> Brave is built on the same engine as Chrome, but it deliberately blurs some measurements: it adds small random changes that differ per website and per browsing session, which Brave calls “farbling”. Reading those measurements would only make the id change for no reason, so WhiskerJS recognises Brave and skips them. Since Brave 1.93 every Brave user also reports the same generic graphics card details, which say nothing about the device, so those are skipped too: twelve components in all. Everything else is read as in Chrome.

## At a glance

- Browser class: `brave` (`navigator.brave.isBrave` is a function).
- Excluded components: 12, with ruleId `brave_farbling` (11 of them in the visitorId; `speech_voices` is outside both ids). The whole `gpu` group is excluded, so its structuredId part is the same for every Brave run.
- Chromium APIs: present, as in Chrome.
- Lab sample: none.

## How Brave is recognised

The first step of the class test asks whether `navigator.brave` exists and has an `isBrave` function. Nothing is called or awaited; the presence of the function is enough. This comes first because Brave’s user agent string looks like Chrome’s, so without it Brave would be classed `chromium` and the farbled values would be read.

## What works

- **Chromium-only components**: [`ua_client_hints`](https://whiskerjs.com/signals/ua-client-hints/) and [`ua_versions`](https://whiskerjs.com/signals/ua-versions/) report the brands and versions Brave gives; [`heap_limit`](https://whiskerjs.com/signals/heap-limit/) from `performance.memory`. [`device_memory`](https://whiskerjs.com/signals/device-memory/) and [`keyboard_layout`](https://whiskerjs.com/signals/keyboard-layout/) are Chromium APIs too, but Brave’s protections may change or turn off such values, so treat them as variable.
- [`font_preferences`](https://whiskerjs.com/signals/font-preferences/), Math, codecs, locale, screen, color depth, CPU architecture, touch support, storage presence, permission states and the display preference queries.
- [`content_blockers`](https://whiskerjs.com/signals/content-blockers/): Brave blocks ads by default, so its cosmetic filtering may hide some of the eight bait elements; the component lists which. It is outside both ids, because blockers are switched per site.

## What is unavailable or blocked

- [`audio_latency`](https://whiskerjs.com/signals/audio-latency/) is `unavailable` / `skipped` on desktop and read on Android (Android user agent).
- [`apple_pay`](https://whiskerjs.com/signals/apple-pay/) and [`private_click`](https://whiskerjs.com/signals/private-click/) report the plain value `none`.
- [`canvas_readback`](https://whiskerjs.com/signals/canvas-readback/) and [`noise_profile`](https://whiskerjs.com/signals/noise-profile/) are not excluded; with farbling they typically report `altered` and `seeded` for the APIs Brave randomises, which is itself a useful diagnostic.

## What WhiskerJS excludes and why

| Component | Why it is left out |
| --- | --- |
| [`audio`](https://whiskerjs.com/signals/audio/) | audio output is farbled |
| [`canvas`](https://whiskerjs.com/signals/canvas/) | canvas readback is farbled |
| [`fonts`](https://whiskerjs.com/signals/fonts/) | Brave randomises the visible font set |
| [`fonts_canvas`](https://whiskerjs.com/signals/fonts-canvas/) | the same font set, probed through the canvas (added to the table at signalsVersion 3) |
| [`hardware_concurrency`](https://whiskerjs.com/signals/hardware-concurrency/) | the CPU thread count is farbled |
| [`plugins`](https://whiskerjs.com/signals/plugins/) | the plugin list is farbled |
| [`speech_voices`](https://whiskerjs.com/signals/speech-voices/) | the voice list is farbled (added at signalsVersion 6) |
| [`webgl_image`](https://whiskerjs.com/signals/webgl-image/) | WebGL readback is farbled |
| [`webgl_params`](https://whiskerjs.com/signals/webgl-params/) | since Brave 1.93 every user reports generic WebGL parameters (added at signalsVersion 6) |
| [`webgl_renderer`](https://whiskerjs.com/signals/webgl-renderer/) | since Brave 1.93 every user reports one generic renderer string (added at signalsVersion 6) |
| [`webgl_vendor`](https://whiskerjs.com/signals/webgl-vendor/) | since Brave 1.93 every user reports one generic vendor string (added at signalsVersion 6) |
| [`webgpu`](https://whiskerjs.com/signals/webgpu/) | since Brave 1.93 the WebGPU adapter info has its fields emptied (added at signalsVersion 6) |

All twelve report `excluded` with ruleId `brave_farbling`. In the visitorId and structuredId they count as “missing”, the same in every run. The rows reflect Brave’s public documentation of farbling, as noted in `src/exclusions.ts`. [`languages`](https://whiskerjs.com/signals/languages/) stays in: Brave shortens the list but does not randomise it in script. If Brave changes what it farbles, the table changes and `signalsVersion` goes up.

## Integrity flags to expect

- [`engine_mismatch`](https://whiskerjs.com/docs/integrity/#engine_mismatch) expects V8 behaviour for the `brave` class (a 33-character `eval` source, `productSub` `20030107`), which Brave has.
- [`native_tampering`](https://whiskerjs.com/docs/integrity/#native_tampering), [`prototype_lie`](https://whiskerjs.com/docs/integrity/#prototype_lie) and [`navigator_own_property`](https://whiskerjs.com/docs/integrity/#navigator_own_property) are designed to spot scripts that override functions. Whether Brave’s built-in protections trip any of them has not been measured; treat these flags on Brave traffic with the same care as for privacy extensions.
- [`automation_webdriver`](https://whiskerjs.com/docs/integrity/#automation_webdriver) under automation, as in Chrome.
- [`media_query_mismatch`](https://whiskerjs.com/docs/integrity/#media_query_mismatch) does not run for the `brave` class, and with the WebGL renderer excluded neither [`software_renderer`](https://whiskerjs.com/docs/integrity/#software_renderer) nor [`vm_renderer`](https://whiskerjs.com/docs/integrity/#vm_renderer) can fire.
- Brave’s noise is never a flag: it shows in `noise_profile` and in the excluded components.

## Early lab observations

There is no lab data for Brave yet. The early, small sample at signalsVersion 1 covered Mac Chrome, Mac Safari, iPhone Chrome and iPhone Safari only, and none of those had exclusions.

## Testing it yourself

Open the [live demo](https://whiskerjs.com/demo/) in Brave: `browser_class` should say `brave` and the twelve components above show `excluded`. Change the Shields setting for the demo site and run again to see `content_blockers` and `canvas_readback` move. The [lab mode](https://whiskerjs.com/demo/#lab) copies one line per run. Nothing is sent anywhere.

## Support table for Brave

Expected status of every component in this browser at signalsVersion 9, from [the support matrix](https://whiskerjs.com/browsers/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.

| Component | Status | Basis | Note |
| --- | --- | --- | --- |
| [`apple_pay`](https://whiskerjs.com/signals/apple-pay/) | none | code | No ApplePaySession: reports the plain value none. |
| [`audio`](https://whiskerjs.com/signals/audio/) | excluded | code | Exclusion table: brave_farbling (Brave adds per-site, per-session noise). |
| [`audio_latency`](https://whiskerjs.com/signals/audio-latency/) | varies | code | Read on Android (Android user agent), skipped on desktop. |
| [`browser_class`](https://whiskerjs.com/signals/browser-class/) | ok | code | Class brave (navigator.brave.isBrave is a function). |
| [`canvas`](https://whiskerjs.com/signals/canvas/) | excluded | code | Exclusion table: brave_farbling (Brave adds per-site, per-session noise). |
| [`canvas_readback`](https://whiskerjs.com/signals/canvas-readback/) | varies | code | Not excluded; Brave's canvas farbling can make it report altered. |
| [`color_depth`](https://whiskerjs.com/signals/color-depth/) | ok | code |   |
| [`color_gamut`](https://whiskerjs.com/signals/color-gamut/) | ok | code |   |
| [`content_blockers`](https://whiskerjs.com/signals/content-blockers/) | ok | code | Brave's built-in blocking may hide some baits; reports the hidden bait names or none. |
| [`cpu_architecture`](https://whiskerjs.com/signals/cpu-architecture/) | ok | code |   |
| [`device_memory`](https://whiskerjs.com/signals/device-memory/) | varies | code | Chromium API; Brave's fingerprinting protections may change the value. From Chromium 146 the value carries the tag ;c146. |
| [`dynamic_range`](https://whiskerjs.com/signals/dynamic-range/) | ok | code |   |
| [`font_preferences`](https://whiskerjs.com/signals/font-preferences/) | ok | code |   |
| [`fonts`](https://whiskerjs.com/signals/fonts/) | excluded | code | Exclusion table: brave_farbling (Brave adds per-site, per-session noise). |
| [`fonts_canvas`](https://whiskerjs.com/signals/fonts-canvas/) | excluded | code | Exclusion table: brave_farbling (Brave adds per-site, per-session noise). |
| [`forced_colors`](https://whiskerjs.com/signals/forced-colors/) | ok | code |   |
| [`hardware_concurrency`](https://whiskerjs.com/signals/hardware-concurrency/) | excluded | code | Exclusion table: brave_farbling (Brave adds per-site, per-session noise). |
| [`heap_limit`](https://whiskerjs.com/signals/heap-limit/) | ok | code |   |
| [`intl_locale`](https://whiskerjs.com/signals/intl-locale/) | ok | code |   |
| [`keyboard_layout`](https://whiskerjs.com/signals/keyboard-layout/) | varies | code | Chromium API; Brave may turn it off. |
| [`languages`](https://whiskerjs.com/signals/languages/) | ok | code |   |
| [`math`](https://whiskerjs.com/signals/math/) | ok | code |   |
| [`media_codecs`](https://whiskerjs.com/signals/media-codecs/) | ok | code |   |
| [`noise_profile`](https://whiskerjs.com/signals/noise-profile/) | ok | code | Typically seeded for the APIs Brave farbles. |
| [`pdf_viewer`](https://whiskerjs.com/signals/pdf-viewer/) | ok | code |   |
| [`permissions`](https://whiskerjs.com/signals/permissions/) | ok | code |   |
| [`pixel_ratio`](https://whiskerjs.com/signals/pixel-ratio/) | ok | code |   |
| [`platform`](https://whiskerjs.com/signals/platform/) | ok | code |   |
| [`plugins`](https://whiskerjs.com/signals/plugins/) | excluded | code | Exclusion table: brave_farbling (Brave adds per-site, per-session noise). |
| [`prefers_contrast`](https://whiskerjs.com/signals/prefers-contrast/) | ok | code |   |
| [`private_click`](https://whiskerjs.com/signals/private-click/) | none | code | No attributionSourceId on anchors: reports the plain value none. |
| [`reduced_motion`](https://whiskerjs.com/signals/reduced-motion/) | ok | code |   |
| [`screen_frame`](https://whiskerjs.com/signals/screen-frame/) | ok | code |   |
| [`screen_resolution`](https://whiskerjs.com/signals/screen-resolution/) | ok | code |   |
| [`speech_voices`](https://whiskerjs.com/signals/speech-voices/) | excluded | code | Exclusion table: brave_farbling (Brave farbles the voice list). |
| [`storage`](https://whiskerjs.com/signals/storage/) | ok | code |   |
| [`time_zone`](https://whiskerjs.com/signals/time-zone/) | ok | code |   |
| [`touch_support`](https://whiskerjs.com/signals/touch-support/) | ok | code |   |
| [`ua_client_hints`](https://whiskerjs.com/signals/ua-client-hints/) | ok | code | Chromium API; the brand list is whatever Brave reports. |
| [`ua_versions`](https://whiskerjs.com/signals/ua-versions/) | ok | code | Chromium API; not in either id. |
| [`vendor`](https://whiskerjs.com/signals/vendor/) | ok | code |   |
| [`vendor_flavors`](https://whiskerjs.com/signals/vendor-flavors/) | ok | code |   |
| [`webgl_image`](https://whiskerjs.com/signals/webgl-image/) | excluded | code | Exclusion table: brave_farbling (Brave adds per-site, per-session noise). |
| [`webgl_params`](https://whiskerjs.com/signals/webgl-params/) | excluded | code | Exclusion table: brave_farbling (since Brave 1.93 every user reports generic values). |
| [`webgl_renderer`](https://whiskerjs.com/signals/webgl-renderer/) | excluded | code | Exclusion table: brave_farbling (since Brave 1.93 every user reports generic values). |
| [`webgl_vendor`](https://whiskerjs.com/signals/webgl-vendor/) | excluded | code | Exclusion table: brave_farbling (since Brave 1.93 every user reports generic values). |
| [`webgpu`](https://whiskerjs.com/signals/webgpu/) | excluded | code | Exclusion table: brave_farbling (since Brave 1.93 the adapter fields are emptied). |

## Questions and answers

### Does WhiskerJS try to get around Brave's farbling?

No. Components Brave randomises on purpose are not collected; they report excluded with ruleId brave_farbling. Working around a privacy feature is out of scope for the library.

### How does WhiskerJS recognise Brave?

By navigator.brave.isBrave being a function. Brave's user agent looks like Chrome's, so the user agent alone would not tell it apart.

### Is the id stable in Brave?

The excluded components count as missing in every run, so they do not change the id. Other components can still change, and Brave may alter more values than the twelve in the table; the known-answer probes then turn canvas, webgl_image or audio noisy instead of letting them change the id. No stability figure has been measured.

## Related pages

- [Farbling](https://whiskerjs.com/glossary/#farbling): Brave's per-site, per-session noise.
- [Firefox resistFingerprinting, Tor and Mullvad](https://whiskerjs.com/browsers/firefox-rfp-and-tor/): Another browser class with exclusions.
- [noise_profile](https://whiskerjs.com/signals/noise-profile/): Which known-answer probes see Brave's noise.
- [The exclusion table](https://whiskerjs.com/docs/how-it-works/#the-exclusion-table): Every row and its ruleId.
- [content_blockers](https://whiskerjs.com/signals/content-blockers/): Brave blocks ads by default.
- [Chrome (desktop)](https://whiskerjs.com/browsers/chrome-desktop/): The same engine, without farbling.
