| Component name | fonts_canvas |
|---|---|
| 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 3 |
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
- Measure the sample text width in each of the three generic fallbacks.
- For each family and each fallback, measure the text with the family first in the font stack.
- A family is present when any of its three widths differs from the matching fallback width.
- Found families are sorted, joined and hashed; with none found the plain word
noneis 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
okin 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.