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.isBraveis a function). - Excluded components: 12, with ruleId
brave_farbling(11 of them in the visitorId;speech_voicesis outside both ids). The wholegpugroup 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_hintsandua_versionsreport the brands and versions Brave gives;heap_limitfromperformance.memory.device_memoryandkeyboard_layoutare Chromium APIs too, but Brave’s protections may change or turn off such values, so treat them as variable. font_preferences, Math, codecs, locale, screen, color depth, CPU architecture, touch support, storage presence, permission states and the display preference queries.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_latencyisunavailable/skippedon desktop and read on Android (Android user agent).apple_payandprivate_clickreport the plain valuenone.canvas_readbackandnoise_profileare not excluded; with farbling they typically reportalteredandseededfor the APIs Brave randomises, which is itself a useful diagnostic.
What WhiskerJS excludes and why
| Component | Why it is left out |
|---|---|
audio |
audio output is farbled |
canvas |
canvas readback is farbled |
fonts |
Brave randomises the visible font set |
fonts_canvas |
the same font set, probed through the canvas (added to the table at signalsVersion 3) |
hardware_concurrency |
the CPU thread count is farbled |
plugins |
the plugin list is farbled |
speech_voices |
the voice list is farbled (added at signalsVersion 6) |
webgl_image |
WebGL readback is farbled |
webgl_params |
since Brave 1.93 every user reports generic WebGL parameters (added at signalsVersion 6) |
webgl_renderer |
since Brave 1.93 every user reports one generic renderer string (added at signalsVersion 6) |
webgl_vendor |
since Brave 1.93 every user reports one generic vendor string (added at signalsVersion 6) |
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 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_mismatchexpects V8 behaviour for thebraveclass (a 33-characterevalsource,productSub20030107), which Brave has.native_tampering,prototype_lieandnavigator_own_propertyare 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_webdriverunder automation, as in Chrome.media_query_mismatchdoes not run for thebraveclass, and with the WebGL renderer excluded neithersoftware_renderernorvm_renderercan fire.- Brave’s noise is never a flag: it shows in
noise_profileand 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 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 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 (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 | none | code | No ApplePaySession: reports the plain value none. |
audio | excluded | code | Exclusion table: brave_farbling (Brave adds per-site, per-session noise). |
audio_latency | varies | code | Read on Android (Android user agent), skipped on desktop. |
browser_class | ok | code | Class brave (navigator.brave.isBrave is a function). |
canvas | excluded | code | Exclusion table: brave_farbling (Brave adds per-site, per-session noise). |
canvas_readback | varies | code | Not excluded; Brave's canvas farbling can make it report altered. |
color_depth | ok | code | |
color_gamut | ok | code | |
content_blockers | ok | code | Brave's built-in blocking may hide some baits; reports the hidden bait names or none. |
cpu_architecture | ok | code | |
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 | ok | code | |
font_preferences | ok | code | |
fonts | excluded | code | Exclusion table: brave_farbling (Brave adds per-site, per-session noise). |
fonts_canvas | excluded | code | Exclusion table: brave_farbling (Brave adds per-site, per-session noise). |
forced_colors | ok | code | |
hardware_concurrency | excluded | code | Exclusion table: brave_farbling (Brave adds per-site, per-session noise). |
heap_limit | ok | code | |
intl_locale | ok | code | |
keyboard_layout | varies | code | Chromium API; Brave may turn it off. |
languages | ok | code | |
math | ok | code | |
media_codecs | ok | code | |
noise_profile | ok | code | Typically seeded for the APIs Brave farbles. |
pdf_viewer | ok | code | |
permissions | ok | code | |
pixel_ratio | ok | code | |
platform | ok | code | |
plugins | excluded | code | Exclusion table: brave_farbling (Brave adds per-site, per-session noise). |
prefers_contrast | ok | code | |
private_click | none | code | No attributionSourceId on anchors: reports the plain value none. |
reduced_motion | ok | code | |
screen_frame | ok | code | |
screen_resolution | ok | code | |
speech_voices | excluded | code | Exclusion table: brave_farbling (Brave farbles the voice list). |
storage | ok | code | |
time_zone | ok | code | |
touch_support | ok | code | |
ua_client_hints | ok | code | Chromium API; the brand list is whatever Brave reports. |
ua_versions | ok | code | Chromium API; not in either id. |
vendor | ok | code | |
vendor_flavors | ok | code | |
webgl_image | excluded | code | Exclusion table: brave_farbling (Brave adds per-site, per-session noise). |
webgl_params | excluded | code | Exclusion table: brave_farbling (since Brave 1.93 every user reports generic values). |
webgl_renderer | excluded | code | Exclusion table: brave_farbling (since Brave 1.93 every user reports generic values). |
webgl_vendor | excluded | code | Exclusion table: brave_farbling (since Brave 1.93 every user reports generic values). |
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.