Signal · signalsVersion 5

fonts_canvas

The same 54 font families, detected with canvas text measurement instead of page layout.

fonts_canvas at a glance (from the library code, signalsVersion 5)
Component namefonts_canvas
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 3
In plain words

This is a second way of checking which fonts are installed. Instead of placing text in the page, WhiskerJS asks an invisible canvas how wide a word would be in each font. If a font is missing, the browser falls back to a plain font and the width is the same as the plain font’s. Only a short code of the found fonts is kept.

What it reads

It creates a detached <canvas> and its 2D context. For each of the same 54 families used by fonts, it sets ctx.font to 72px '<family>', <fallback> for the fallbacks monospace, sans-serif and serif, calls measureText('mmmmmmmmmmlliWWQ@#0OfiflK') and compares the width with the fallback alone.

How the technique works

  1. Measure the sample text width in each of the three generic fallbacks.
  2. For each family and each fallback, measure the text with the family first in the font stack.
  3. A family is present when any of its three widths differs from the matching fallback width.
  4. Found families are sorted, joined and hashed; with none found the plain word none is reported.

Because measureText uses the font engine directly, no element is added to the page and the page’s CSS cannot change the measurement. If all fallback widths are zero or not numbers, there is no usable text layout and the result is unavailable / no_layout.

Why it helps

It sees the same font differences as fonts through a different path. Canvas text measurement does not depend on the document’s layout or styles, so it still works when the page CSS would disturb span measurement. It sits in the fonts group.

Stability

Like fonts: it changes when one of the 54 families is installed or removed, or when an update changes which system fonts the browser exposes.

Browser support and what blocks it

  • ok in all browsers with a 2D canvas.
  • Brave: excluded / brave_farbling (added to the Brave row in signalsVersion 3), because Brave randomises the visible font set.
  • Not excluded under Firefox resistFingerprinting.

Privacy

Same as fonts: it shows which of 54 common families are present, as a digest only. Nothing is drawn on screen, no permission, no prompt, no request.

How WhiskerJS handles noise and failures

Status ruleId When
ok none digest of the found list, or plain none
unavailable no_document, no_api, no_context no document, no getContext, no 2D context with measureText
unavailable no_layout the fallback widths were zero or invalid
unavailable no_digest no Web Crypto
blocked security_error, threw a canvas call threw
excluded brave_farbling, caller_excluded exclusion table or your list
timed_out timeout, aborted 1000 ms inner timeout, budget, abort

Example value

From headless Chromium, Firefox and WebKit test browsers in a container (all three found the same families):

"fonts_canvas": { "status": "ok", "value": "e2727c20c7bf4d2b9aa270f3427907a0", "hashed": true }

Questions and answers

Why measure fonts twice?

The span method in fonts depends on page layout and CSS; the canvas method does not touch the DOM. Two independent methods make the fonts group more robust, and a difference between them can itself be telling.

Can fonts and fonts_canvas give the same digest?

Yes. Both hash the sorted list of found families in the same form, so when both methods find the same families the digests are equal, as in the container test runs used for these pages.