| Component name | fonts |
|---|---|
| In the visitorId | yes |
| structuredId group | fonts |
| Hashing | always a digest, except the plain value none when nothing is found |
| Excluded under | Brave (brave_farbling) |
| Inner timeout | 1000 ms |
| Added in | signalsVersion 1 |
Different computers have different fonts installed, depending on the operating system, its language packs and the software people install. A web page cannot ask for the font list, but it can write a word in a font and see whether its size changes compared with a plain fallback font. WhiskerJS does this, invisibly, for 54 common fonts and keeps only a short code of which ones it found.
What it reads
The 54 families are a fixed list of fonts common on Windows, macOS, Linux, Android and iOS, for example Arial Black, Calibri, Consolas, Helvetica Neue, Menlo, Noto Color Emoji, Roboto, Segoe UI, SimSun, Ubuntu and Yu Gothic. For each family WhiskerJS creates three <span> elements with the sample text mmmmmmmmmmlliWWQ@#0OfiflK at 64 px, set to '<family>', monospace, '<family>', sans-serif and '<family>', serif, plus one span per plain fallback. All spans sit in one container placed at left: -9999px with visibility: hidden, appended to document.body, measured, and removed in a finally block.
How the technique works
- Measure the width and height (
offsetWidth,offsetHeight) of the text in each generic fallback alone. - Measure the same text in each family with each fallback behind it.
- If the family is missing, the browser uses the fallback and the size is identical. If it is installed, at least one width or height differs, and the family counts as present.
- The found families are sorted and joined; the list is hashed. When none is found, the component reports the plain word
noneinstead of a digest.
All spans are created first and measured afterwards, so the browser lays out the container once.
Why it helps
Installed fonts vary a lot: an office suite, design software or a language pack each add families, and each operating system ships its own set. The list of found families is one of the more distinguishing values in the fonts group.
Stability
It changes when the user installs or removes one of the 54 fonts, when an operating system update adds or removes one, or when a browser restricts which system fonts pages can use. Page zoom does not change which fonts are found.
Browser support and what blocks it
okin every browser with a document and layout.- Brave:
excluded/brave_farbling, because Brave randomises the set of fonts visible to pages. - Browsers or privacy settings that limit pages to a small allowlist of fonts make the found list short; it is still read and reported. Firefox resistFingerprinting is not excluded for this component.
unavailable/no_documentwithoutdocument.body(for example when collecting before the body exists),no_layoutwhen nothing is laid out.
Privacy
It reveals which of 54 well-known fonts are present, which can hint at the operating system, languages or installed software. Only a digest of the found list leaves the library. The spans are invisible, exist only for the measurement and are removed at once. No permission, no prompt, no request.
How WhiskerJS handles noise and failures
| Status | ruleId | When |
|---|---|---|
ok |
none | a digest of the found list, or plain none |
unavailable |
no_document |
no document, no document.body |
unavailable |
no_layout |
the fallback spans had no width |
unavailable |
no_digest |
no Web Crypto |
blocked |
security_error, threw |
the DOM work threw |
excluded |
brave_farbling, caller_excluded |
exclusion table or your list |
timed_out |
timeout, aborted |
1000 ms inner timeout, budget, abort |
Example value
From a headless Chromium test browser in a container:
"fonts": { "status": "ok", "value": "e2727c20c7bf4d2b9aa270f3427907a0", "hashed": true }
Questions and answers
Can WhiskerJS list every font on my computer?
No. It only tests a fixed list of 54 common families and reports a digest of the ones it found. Fonts outside the list are never seen.
Why is fonts unavailable with no_layout?
The test spans were added but the page laid nothing out, for example because the body has display none. Without layout there are no widths to compare.
Why are there two font signals?
fonts measures HTML spans, which needs page layout; fonts_canvas measures text on a canvas, which page CSS cannot disturb. They detect the same 54 families in two independent ways.