Signal · signalsVersion 14

text_metrics

Every TextMetrics field (width, bounding boxes, baselines) of fixed strings in four generic font families, measured twice, as a digest. Not in either id.

text_metrics at a glance (from the library code, signalsVersion 14)
Component nametext_metrics
In the visitorIdno (reported, kept out of both ids)
structuredId groupnone
Hashingalways a digest (first 128 bits of SHA-256)
Excluded underBrave (brave_farbling)
Inner timeout1000 ms
Added insignalsVersion 11
Collector revision1 (reported as revision with every result)
In plain words

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:

  1. Cwm fjordbank glyphs vext quiz (Latin letters)
  2. AVAWAy fi ffl Tj 0123456789 (kerning pairs, ligatures, digits)
  3. 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

  • ok wherever 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_context without 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.