| Component name | font_preferences |
|---|---|
| In the visitorId | yes |
| structuredId group | fonts |
| Hashing | always a digest (first 128 bits of SHA-256) |
| Excluded under | no browser class |
| Inner timeout | 1000 ms |
| Added in | signalsVersion 3 |
Browsers let people choose their default fonts and a minimum text size, and the operating system has its own system font. WhiskerJS writes one sentence in each of these defaults, invisibly, and measures how wide it is. The widths follow the user’s settings and the system, and only a short code of them is kept.
What it reads
Seven hidden <span> elements with the text “Whiskers quietly judge 0123456789”, in one off-screen container (left: -9999px, visibility: hidden) appended to the body and removed in a finally block:
| Name | Style |
|---|---|
default |
no font family (the browser default) |
serif |
font-family: serif |
sans |
font-family: sans-serif |
mono |
font-family: monospace |
system |
font-family: system-ui |
apple |
font-family: -apple-system |
min |
browser default family at font-size: 1px |
Each span’s offsetWidth is read and the seven widths are written as default=…;serif=…;…;min=… and hashed.
How the technique works
Each generic family name maps to a concrete font chosen by the browser settings and the operating system. Measuring the same text in each reveals that mapping without asking for it. -apple-system resolves only on Apple platforms, so its width differs elsewhere. The 1 px span is drawn at the browser’s minimum font size. If every width is zero, nothing was laid out and the result is unavailable / no_layout.
Why it helps
Users rarely change these settings, but when they do the values differ clearly, and operating systems differ in their default and system fonts. It is part of the fonts group of the structuredId.
Stability
It changes when the user changes the default fonts, the default font size or the minimum font size, or when an operating system or browser update changes the system font or the fonts the generic names map to. The page’s own styles do not matter much, because each span sets its own family and the container sits outside the page flow.
Browser support and what blocks it
ok in every browser with a document and layout; not in the exclusion table. unavailable / no_document before document.body exists, no_layout when the body is not laid out. See the browser pages for each family.
Privacy
It reveals font settings, not content. Only a digest leaves the library. The spans are invisible and removed at once. No permission, no prompt, no request.
How WhiskerJS handles noise and failures
| Status | ruleId | When |
|---|---|---|
ok |
none | value is a digest |
unavailable |
no_document |
no document or body |
unavailable |
no_layout |
all widths were zero |
unavailable |
no_digest |
no Web Crypto |
blocked |
security_error, threw |
the DOM work threw |
excluded |
caller_excluded |
your exclude list |
timed_out |
timeout, aborted |
1000 ms inner timeout, budget, abort |
Example value
From a headless Chromium test browser in a container:
"font_preferences": { "status": "ok", "value": "374bafbd56b68b15ac31e242b3cc90f7", "hashed": true }
Questions and answers
What does the 1 px measurement show?
Browsers enforce a minimum font size setting. Text asked for at 1 px is drawn at that minimum, so its width reveals the setting.
Is font_preferences excluded anywhere?
No. It is not in the exclusion table, so it is read in every browser class, including Brave and Firefox resistFingerprinting.