Our home-made test page gave Chrome the same ID, e08e953c, on 5 of 5 visits, and every visit started with a brand new empty profile and zero cookies. Brave, with its default settings, got 5 different IDs in 5 visits on the same Mac.
So clearing cookies changes nothing about what this page sees. We ran this ourselves; below is the method, the reason the two browsers differ, and the limits.
How we measured it
The page reads 7 things from the browser, joins them, and hashes them with SHA-256. The first 8 hex characters are the ID. Two bytes of the ID pick a two-word nickname from two lists of 32 words, so it is easy to read on screen. Chrome was "Bold Llama".
// inside an async function; c is a <canvas> we drew shapes, text and an emoji on
const hex = async s => [...new Uint8Array(await crypto.subtle.digest('SHA-256',
new TextEncoder().encode(s)))].map(b => b.toString(16).padStart(2, '0')).join('');
const canvas = await hex(c.toDataURL());
const ac = new OfflineAudioContext(1, 44100, 44100);
const o = ac.createOscillator(), comp = ac.createDynamicsCompressor();
o.type = 'triangle'; o.frequency.value = 10000;
o.connect(comp); comp.connect(ac.destination); o.start(0);
const buf = (await ac.startRendering()).getChannelData(0);
let sum = 0; for (let i = 4500; i < 5000; i++) sum += Math.abs(buf[i]);
const gl = document.createElement('canvas').getContext('webgl');
const e = gl.getExtension('WEBGL_debug_renderer_info');
const webgl = e ? gl.getParameter(e.UNMASKED_RENDERER_WEBGL) : gl.getParameter(gl.RENDERER);
const parts = {
canvas, audio: String(sum),
screen: `${screen.width}x${screen.height}x${screen.colorDepth}@${devicePixelRatio}`,
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
languages: navigator.languages.join(','),
cpu_cores: String(navigator.hardwareConcurrency),
webgl
};
const id = (await hex(JSON.stringify(parts))).slice(0, 8);
A small Playwright script (run.js) opens the page with chromium.launchPersistentContext(profile, { executablePath, headless: true }). The profile is a fresh fs.mkdtempSync folder on every visit, deleted afterwards.
python3 -m http.server 60309 --bind 127.0.0.1
node run.js chrome http://127.0.0.1:60309/fp.html 6 # 1 warm-up + 5 measured
node run.js brave http://127.0.0.1:60309/fp.html 6
KEEP=1 node run.js brave http://127.0.0.1:60309/fp.html 6 # one kept profile, restart + one reload per visit
Machine: Apple M1 Pro, 16 GB, macOS 26.6.2. Chrome 155.0.8059.40, Brave 155.1.97.56, Playwright 1.64.0, Node v26.8.1. Both browsers ran headless.
The 60-second version:
The numbers
| Run | Cookies and saved data per visit | Different IDs in 5 visits | Names |
|---|---|---|---|
| Chrome, new empty profile each visit | 0 | 1 | Bold Llama, 5 times |
| Brave, new empty profile each visit | 0 | 5 | Zippy Llama, Mighty Mole, Zippy Wombat, Chilly Ferret, Fancy Lobster |
| Brave, same profile, browser restarted each visit | 0 | 5 | Mellow Panda, Fuzzy Owl, Jolly Owl, Shiny Yak, Cranky Pigeon |
In Chrome all 7 parts had one value across the 5 visits. In Brave only 3 parts moved: the canvas hash (5 different values), the audio sum (5), and the CPU core count, which came back as 7, 4, 8, 7, 7. The other 4 stayed the same.
We also opened brave://settings/shields in a new empty profile. "Block fingerprinting" was already on.
Why the two browsers differ
None of the 7 parts is stored in the profile. They describe the hardware and the system, so a new profile reads them the same way. A cookie is something the site puts on your disk, a fingerprint is something the site asks your machine. Mozilla's Tom Ritter wrote in the Firefox fingerprinting post (10 Nov 2025) that the technique "lets websites identify you even when cookies are blocked or you're in private browsing".
Brave does not block the reads. Per the Brave fingerprinting wiki, it adds small random changes to some API values, from "a seed that changes per session, per site (eTLD+1) and per storage area". A hash over many parts breaks as soon as one part moves, and here three of them moved.
There is also a consent angle. The UK regulator ICO noted on 19 Dec 2024 that from 16 February 2025 Google no longer prohibits fingerprinting in its advertising products, and that Google itself had earlier said users "cannot easily consent to it as they would cookies".
Limits you should know
- One machine, one test, 5 measured visits per run.
- Both browsers ran headless through Playwright with default launch flags. Chrome reported the 1080x1920 viewport as its screen and Brave reported 3840x2160, neither being the real display.
- The page was served from 127.0.0.1. It is a 7-part toy, not an ad tracker. Real trackers can use more signals, including your IP address, which Brave does not hide.
- We showed the Chrome ID is stable. We did not measure how unique it is: two identical Macs could get the same one.
- In Brave the ID changed on every browser start. A reload inside one session kept the same ID, 5 of 5 times, which matches the per-session seed. It is not a new name on every page load.
- Firefox with Strict protection was not tested.
- Our first full run was thrown away: the nickname list had the word "Brave" in it, so Chrome was named "Brave Llama". We changed it to "Bold" and ran everything again; the pattern was the same.
Next we want to run the page in a normal visible window, and on a second identical Mac to see if the IDs collide. If you run it, which of the 7 parts changes on your setup?
Top comments (0)