When Pixelscan says masking detected and fonts are involved, the likely cause is a font list with gaps that the claimed machine would not have. That was our answer, found by bisecting one profile on our own engine: canvas, WebGL, audio and client-rect noise were all fine, and the verdict came entirely from hiding a small share of font families. Jittering glyph widths was not the signal.
The first run on our own engine
Once Tesseract Chrome, our Chromium, ran at all, the question became what a detector sees. The first pass was one fresh profile with no proxy:
| Detector | Result |
|---|---|
| BrowserScan | pass, 95% |
| IPHey | attention, Unreliable, MX 90/100 |
| Pixelscan | fail, fingerprint inconsistent, masking detected |
| CreepJS | unread, no trust score rendered |
Two of those were worse than the stock-browser build had scored, so something in our own patches was costing us. The engine has a switch for exactly this case: --disable-spoofing takes a list of surfaces and turns each one back to the real value. That makes a bisect possible.
Bisecting one profile, with a control at both ends
The discipline matters more than the switch. We used one profile, patched in place through its extra launch arguments and restarted between rows, because two freshly generated profiles differ in every column and comparing them tells you nothing. And we ran the unchanged profile first and last, so we would know if Pixelscan itself drifted during the run.
| Run | Pixelscan |
|---|---|
| baseline (control) | fail |
| every surface off | pass |
| baseline again (control) | fail |
The controls agreed with each other, so the middle row meant something. Then the split:
| Surfaces turned off | Pixelscan |
|---|---|
| audio, canvas, clientrects, font, gpu, screen | pass |
| everything else | fail |
| font only | pass |
| canvas only | fail |
| gpu only | fail |
| audio only | fail |
| screen only | fail |
| client rects only | fail |
One row passes with a single surface off, and it is fonts. The surfaces we had spent months on, canvas, WebGL, audio and client rects, are all still on in the row that passes. The noise was fine. The font list was not.
Every profile is drawn from one real computer, not from separate listsIn Mosaic: Machines that exist. GPU, screen, CPU and fonts from one real device. Free holds 3 profiles. Pro is $49 a month for any number.
Mosaic is a desktop app
Email yourself the link and open it on your computer.
How it works: Machines that exist
Two font patches behind one switch
We nearly stopped there and deleted the wrong thing. The engine had two font patches, and both sat behind the one font token: one hid a small share of installed families per profile, about 2%, and the other jittered the advance width of glyphs. Turning font off turned off both.
The jitter has its own off switch by accident. It reads a seed from --fingerprint-font-spacing-seed, and a seed that is not a number fails to parse, so the function returns the real width. That let us hold the two apart:
| Glyph jitter | Family hiding | Pixelscan |
|---|---|---|
| off | on | fail |
| off | off | pass |
| on | on | fail |
| off | on | fail (repeat) |
The hiding is the signal and the jitter is not. Patch 0131 removes the hiding and keeps the jitter. That is the better trade both ways: the jitter still gives each profile its own measured text widths, and it does not change which fonts exist.
Why hiding fonts reads as masking
Pixelscan's collector, fptc.min.js, is only lightly obfuscated, and it declares what it gathers: audio, canvasHash, fonts, hardwareConcurrency, language, navigatorPlatform, screenResolution, secCh, timezone, ua, webDriver, webglHash and webglMeta. The verdict is computed on their server, so we can say what it reads, not exactly how it weighs it.
What the bisect says is enough, though. A macOS font list missing two families at random is not a rare Mac. It is a Mac nobody owns, because the families macOS ships are the same on every Mac. Each individual omission was plausible, and together they described no machine. It is the same mistake as an Apple M3 Max GPU beside a 13-inch MacBook Air screen, made two per cent at a time.
The same check explains the larger version of this verdict. A profile that claims a different operating system from the computer it runs on gets that computer's fonts, because fonts come from the real machine and no switch reaches them. On a Mac, claiming Linux read masking detected with 1 font against 159, which is the case in a Mac makes Mac profiles.
What it cost, and what it did not
Removing the hiding has a price we accepted on purpose. Two profiles on one Mac now report the same installed fonts, as they already report the same GPU vendor, operating system and TLS handshake. A shared truth is better than a private contradiction, and what still separates profiles is the canvas and audio seeds, screen, locale, timezone, the glyph widths, and above all the cookie jar.
How a different detector, CreepJS, decides that a value is a lie is in what a CreepJS lie is. The values Tesseract Chrome changes, and the ones it leaves true, are on real fingerprints. A detector verdict is one dated measurement on one machine, and we treat it that way, which is why this one was settled with a bisect rather than a theory.





