| Component name | text_metrics |
|---|---|
| In the visitorId | no (reported, kept out of both ids) |
| structuredId group | none |
| Hashing | always a digest (first 128 bits of SHA-256) |
| Excluded under | Brave (brave_farbling) |
| Inner timeout | 1000 ms |
| Added in | signalsVersion 11 |
| Collector revision | 1 (reported as revision with every result) |
How text is drawn depends on the fonts installed, their versions and the text engine of the system. A canvas can tell a page exactly how wide a piece of text is and how far it reaches above and below the line. WhiskerJS measures a few fixed sentences in the browser’s standard font families and turns all the numbers into one short code.
What it reads
CanvasRenderingContext2D.measureText() on a canvas of its own, for three fixed strings:
Cwm fjordbank glyphs vext quiz(Latin letters)AVAWAy fi ffl Tj 0123456789(kerning pairs, ligatures, digits)- a line of Cyrillic, Hebrew, Arabic, Devanagari, Chinese, Korean and Thai
each in 16px sans-serif, 16px serif, 16px monospace and 16px system-ui. From each measurement it takes every TextMetrics field: width, actualBoundingBoxLeft, actualBoundingBoxRight, actualBoundingBoxAscent, actualBoundingBoxDescent, fontBoundingBoxAscent, fontBoundingBoxDescent, emHeightAscent, emHeightDescent, hangingBaseline, alphabeticBaseline and ideographicBaseline. A field the browser does not offer is left empty.
How the technique works
Each number is rounded to three decimals; the twelve fields of each measurement form one line and the twelve lines are hashed together. The whole measurement runs twice: if the two passes differ, the component is noisy / read_varies. Nothing is drawn and nothing is added to the page. Generic families cannot name a page’s @font-face rule, so measuring never starts a font download.
Why it helps
Advance widths alone (as in fonts_canvas) mostly say which fonts are installed. Bounding boxes and baselines also follow font versions, hinting and the text engine, and the multi-script line shows which fallback fonts the system picks for each script. It is a richer view of the text stack than a width.
Stability
Expected to be stable until fonts or the operating system change. In the hosted CI runs of PR #29 each browser gave the same value in every collection and on both runners. It is reported outside both ids until measured on real devices; it is a candidate for the fonts group. Added in library PR #29 (2026-10-27) at signalsVersion 11: it is outside both ids, so adding it changed no id and needed no version bump.
Browser support and what blocks it
okwherever a 2D canvas exists: Chromium, Chrome, Firefox and WebKit in CI.- Firefox with Fingerprinting Protection or resistFingerprinting gave a different value from plain Firefox in CI (both restrict which system fonts pages see), the same in every collection: a privacy setting changes it, as expected.
- Brave:
excluded/brave_farbling, because Brave shows each site a per-session subset of the system fonts. unavailable/no_contextwithout a 2D canvas.
Privacy
Text measurements reveal installed fonts indirectly, like the other font signals. The value leaves the library only as a digest. No prompt, no request, no font download.
How WhiskerJS handles noise and failures
| Status | ruleId | When |
|---|---|---|
ok |
a digest | |
noisy |
read_varies |
the two passes gave different numbers |
unavailable |
no_document, no_api, no_context |
no document, canvas or 2D context |
unavailable |
no_value |
the metrics held no numbers |
unavailable |
no_digest |
no Web Crypto |
blocked |
threw, security_error |
measureText threw |
excluded |
brave_farbling, caller_excluded |
exclusion table or your exclude list |
timed_out |
timeout, aborted |
budget or abort |
Example value
"text_metrics": { "status": "ok", "value": "c41e8a7d02f9b3651e7c0a4d8b92f6e3", "hashed": true, "durationMs": 2.4, "revision": 1 }
Questions and answers
Can text_metrics download a font?
No. It measures only in the generic families sans-serif, serif, monospace and system-ui, which resolve to fonts installed on the device and can never name a web font of the page.
What if a browser adds noise to measureText?
Every string is measured twice. If the two reads differ the component reports noisy with ruleId read_varies instead of a value that changes on every read.