Defeat fingerprinting
Why it matters Blocking a fingerprinting script works only if your list knows it. The deeper defence is to make the traits themselves useless, so that even a script that runs learns nothing that singles you out. This lab measures how much that actually helps, in bits.
Everyone in the same raincoat
Picture a crowd where everyone wears the same grey raincoat and hat. Spotting one person is hard, not because you cannot see them, but because they all look alike. That is uniformity: the browser reports the same values as everyone else using the defence.
Fingerprint: the set of traits a page can read about your browser (screen size, time zone, fonts, graphics card and so on). Together they can recognise a browser without any cookie.
Uniformity: a defence where the browser reports the same fixed values as every other user of the same defence. Tor Browser is the best-known example; Safari also reduces what it exposes (checked October 2026).
The other main defence is randomisation (Brave and Floati call it farbling): add small, changing noise so the trait differs on every site. This lab tries uniformity.
Measuring in bits
In Leg 1 you met bits of identity: each bit halves the crowd. Here is a fair way to measure them for one person in a crowd:
bits = log2( (crowd size + 1) / (people who look exactly like you + 1) )The + 1 counts you as a member of the crowd. If nobody in a crowd of 5,000 looks like you, that is log2(5001 / 1), about 12.3 bits: unique. If 4,999 people look like you, it is log2(5001 / 5000), almost 0 bits.
The plan
fingerprint_bits(crowd, fp): count the rows equal tofpon every trait inTRAIT_NAMES, then use the formula.make_uniform(traits): return a copy with every trait inUNIFORMreplaced.dict(traits)makes a copy;copy.update(UNIFORM)overwrites those keys.uniform_filter(traits, page): plug it intoBrowser(world, trait_filter=uniform_filter)and every script reads the uniform values.- Measure Sam (
recorded_fingerprints()[0]) in 5,000 synthetic browsers, before and after.
One honest detail in step 4: for the "after" number, apply make_uniform to the whole crowd too. Uniformity only hides you among other people using the same defence. If you alone wear the grey raincoat, you stand out more, not less.
What the numbers will tell you
You will remove several bits, but not all of them. Sam still reports a Linux system and a less common browser, and those two traits alone single out a small group. That is the real lesson of uniformity: it works best when everything is the same, including the browser itself. That is why Tor Browser makes every user look like the same browser on the same kind of system, and why it rounds the window size to a few standard steps (letterboxing).
A common misconception
"Faking a rare value hides me." A made-up value that no real browser reports, or a mix that does not fit together (a Mac graphics card on Windows), can make you more unique. Good defences pick values many real users share, and change them together.
Try it After you pass, remove "timezone" from UNIFORM and run your code again. Watch after_bits rise: one ordinary-looking trait is worth real bits.
In other languages
JavaScript:
const UNIFORM = { screen: "1920x1080", timezone: "Etc/UTC", language: "en-US" };
// Copy, then overwrite: the original object is untouched.
const makeUniform = traits => ({ ...traits, ...UNIFORM });
function fingerprintBits(crowd, fp, names) {
const same = crowd.filter(r => names.every(t => r[t] === fp[t])).length;
return Math.log2((crowd.length + 1) / (same + 1));
}Rust:
fn fingerprint_bits(crowd: &[Vec<String>], fp: &[String]) -> f64 {
let same = crowd.iter().filter(|r| r.as_slice() == fp).count();
((crowd.len() + 1) as f64 / (same + 1) as f64).log2()
}Recap
- Uniformity hides you by making many users report identical values.
- Measure bits as log2((crowd + 1) / (look-alikes + 1)), counting yourself in the crowd.
- It only works among people using the same defence, and leftover traits (browser, system) still leak bits.
- Rare or inconsistent fake values can make you stand out more.
Keep UNIFORM. Write fingerprint_bits(crowd, fp) (count rows equal to fp on every trait in TRAIT_NAMES as same, return log2((len(crowd) + 1) / (same + 1))), make_uniform(traits) (a new dict with every UNIFORM key replaced; the input unchanged) and uniform_filter(traits, page) (returns make_uniform(traits)). Then set before_bits (Sam in crowd), after_bits (uniform Sam in a crowd where every row went through make_uniform) and bits_removed.
This lesson is locked
Lessons open one at a time. Finish the previous lesson to unlock this one.