Signal · signalsVersion 5

fonts

Which of 54 common font families are installed, found by measuring hidden text, reported as a digest.

fonts at a glance (from the library code, signalsVersion 5)
Component namefonts
In the visitorIdyes
structuredId groupfonts
Hashingalways a digest, except the plain value none when nothing is found
Excluded underBrave (brave_farbling)
Inner timeout1000 ms
Added insignalsVersion 1
In plain words

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

  1. Measure the width and height (offsetWidth, offsetHeight) of the text in each generic fallback alone.
  2. Measure the same text in each family with each fallback behind it.
  3. 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.
  4. The found families are sorted and joined; the list is hashed. When none is found, the component reports the plain word none instead 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

  • ok in 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_document without document.body (for example when collecting before the body exists), no_layout when 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.