Websites detect installed fonts by measuring text. A script draws a test string in a generic family, records its width and height, then asks for the same string in a font it wants to test with that generic family as the fallback. If the size changes, the font is installed. If it does not, the browser fell back and the font is missing. No font API is involved, and the page never asks permission.
The measuring trick, step by step
Fingerprinting libraries, FingerprintJS's open-source one among them, run a version of this for each font on their list:
- Make a hidden element holding a string chosen for wide variation between typefaces. The widely copied version uses
mmmmmmmmmmlliat 72px: wide and narrow letters, set large, so a one-pixel difference is easy to see. - Set its
font-familytomonospaceand readoffsetWidthandoffsetHeight. Repeat forsans-serifandserif. These three are the baselines. - For each candidate, set
font-family: 'Candidate', monospaceand measure again, then the same with the other two baselines. - If any of the three measurements differs from its baseline, the candidate is installed.
Three baselines rather than one, because a candidate might happen to have the same metrics as one generic family. It is very unlikely to match all three. The list being tested is the script's own, usually a few dozen to a few hundred names chosen because they are present on some machines and absent on others: Office fonts, Adobe fonts, fonts that come with one OS and not another.
The same test runs on a canvas with measureText, or with getBoundingClientRect on a span. Which method a page uses changes nothing about the answer, because all of them ask the layout engine the same question.
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
How websites detect installed fonts without asking
Every line of that is ordinary page behaviour. Pages set fonts and measure elements all the time to lay themselves out, and a browser cannot refuse to measure text without breaking the web. That is why the trick has outlived the plugins that once listed fonts outright (Flash would hand over the whole list) and why it beats the one API built for the job. Chromium's window.queryLocalFonts() returns installed fonts directly, but it shows the user a permission prompt first, and a script that wants to stay quiet will not ask.
There is a second, quieter version. Instead of asking which fonts exist, a script draws the same characters in the same generic family and records their exact size. Fifield and Egelman's 2015 paper, "Fingerprinting web users through font metrics", did this across Unicode code points and found that the rendered dimensions alone separate many browsers. That measures how the machine renders: which font the OS picks for each character, its hinting and its rasteriser, all down to fractions of a pixel. It is a fingerprint even when two machines have the same font list.
What the answer depends on
Both versions read the real computer. The installed fonts are whatever the operating system shipped plus whatever was installed later, and the metrics come from the platform's own text stack. Neither changes when a browser changes its User-Agent, which is why fonts are where a false OS claim tends to show first. On our own Mac, Pixelscan found 159 fonts on a profile claiming macOS and 1 on the same machine claiming Linux, and called the second one masking. A Mac makes Mac profiles has that measurement.
What we found when we hid fonts
Our engine once tried the obvious counter: hide a small, per-profile share of font families, about 2%, so two profiles on one machine would report different lists. Pixelscan read that as masking. We bisected it on one profile, patched in place, with controls at both ends, and only the font surface flipped the verdict. Then we split the two font patches apart: with the glyph-width jitter switched off and the hiding left on, it still failed, and with both off, it passed. Pixelscan's masking verdict, bisected has the full run.
So the hiding went, in patch 0131. A font list with gaps no real installation has is a contradiction, and a detector that measures a few hundred names will find the gaps. What stayed is the per-profile jitter on glyph advances, set in C++ inside the engine rather than by a script in the page (why that matters). It changes the exact widths a page measures without changing which fonts exist.
The cost is stated in the patch itself: two profiles on one machine now report the same font list, as they already report the same GPU vendor, OS and core count. A shared truth there is better than a private contradiction, and what tells two profiles apart is the rest of the fingerprint and, above all, their cookie jars.
How to see your own
A canvas or font test page on BrowserLeaks or CreepJS will run the measuring trick against your browser and print the families it found. Run it in two browsers on one computer and the lists will usually agree, because they read the same system. Run it on a second computer and they will not.





