| Component name | emoji_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) |
Emoji look different on every system, and each operating system update brings new ones. An emoji the system does not know yet is shown as an empty box or as its separate parts. WhiskerJS measures 30 emoji, old and new, and turns the sizes into one short code that reflects which emoji font, and which version of it, the device has.
What it reads
CanvasRenderingContext2D.measureText() in 32px sans-serif, which falls back to the system emoji font, for 30 fixed emoji: from Unicode 8 to Unicode 16, including zero-width-joiner sequences (for example the phoenix and the lime), a skin-tone modifier, a flag and the newest code points of Unicode 16. From each it takes width and the ink box: actualBoundingBoxLeft, actualBoundingBoxRight, actualBoundingBoxAscent and actualBoundingBoxDescent.
How the technique works
Each number is rounded to three decimals, one line per emoji, and the 30 lines are hashed together. The whole measurement runs twice; if the passes differ the component is noisy / read_varies. Only the generic family sans-serif is named, so no web font of the page is used and nothing is downloaded.
Why it helps
A ZWJ sequence that the emoji font supports measures as one glyph; one it does not support measures as several. The newest emoji draw as a box on older systems. Together the 30 measurements separate operating system versions and emoji fonts (Apple, Google Noto, Microsoft, Samsung and others) more finely than the user agent does.
Stability
Stable until the operating system or its emoji font updates. In the hosted CI runs of PR #29 each browser gave the same value in every collection and on both runners; Firefox reported the same value with and without its privacy settings. Reported outside both ids until measured on real devices. 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.- Brave:
excluded/brave_farbling, as for the other font measurements. unavailable/no_contextwithout a 2D canvas.
Privacy
It reveals the emoji font, which says roughly which operating system version is in use. The value leaves the library only as a digest. No prompt and no request.
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
"emoji_metrics": { "status": "ok", "value": "2b7f0e9c4a61d83e5c0b7a2f9d4e1c68", "hashed": true, "durationMs": 1.6, "revision": 1 }
Questions and answers
Why measure emoji?
Every operating system ships its own emoji font and adds new emoji with updates. Newer emoji that the system does not know are drawn as a box or as separate pieces, which measure differently, so the measurements say which emoji font and version is installed.
Does it draw emoji on the page?
No. measureText only measures; nothing is drawn and nothing is added to the page.