Signal · signalsVersion 14

engine_features

Which of 24 JavaScript built-ins, 8 Intl features and 16 WebAssembly features the engine has, as three bit masks in hex. Reported, not in either id.

engine_features at a glance (from the library code, signalsVersion 14)
Component nameengine_features
In the visitorIdno (reported, kept out of both ids)
structuredId groupnone
Hashingplain value; a digest only when longer than 128 characters or not printable ASCII
Excluded underno browser class
Inner timeout1000 ms
Added insignalsVersion 12
Collector revision1 (reported as revision with every result)
In plain words

Every browser version adds new built-in features to its JavaScript engine. WhiskerJS checks which of a fixed list of 48 features exist (it only looks, it never uses them) and writes the answer as three short codes. Because each feature first shipped in a known version, the codes say which engine versions are possible.

What it reads

Presence only, in three groups (bit 0 first):

  • JavaScript (24): Array.prototype.at, Object.hasOwn, Array.prototype.findLast, Array.prototype.toSorted, String.prototype.isWellFormed, ArrayBuffer.prototype.transfer, Object.groupBy, Promise.withResolvers, Array.fromAsync, Set.prototype.union, Iterator.prototype.map, Promise.try, Atomics.pause, Error.isError, Float16Array, RegExp.escape, Uint8Array.fromBase64, Temporal, Map.prototype.getOrInsert, Iterator.concat, Math.sumPrecise, Iterator.zip, Error.captureStackTrace, Atomics.waitAsync.
  • Intl (8): ListFormat, DisplayNames, Segmenter, supportedValuesOf, NumberFormat.prototype.formatRange, PluralRules.prototype.selectRange, DurationFormat, Locale.prototype.getWeekInfo.
  • WebAssembly (16): multi-value, sign extension, saturating float to int, bulk memory, reference types, fixed-width SIMD, threads (a shared memory), exceptions, tail calls, extended constant expressions, relaxed SIMD, typed function references, garbage collection, multiple memories, 64-bit memory, and exceptions with exnref.

How the technique works

Group How a feature is found
JavaScript and Intl a property lookup; for a function, the engine’s own Function.prototype.toString must print native code
WebAssembly WebAssembly.validate() of a tiny module that uses the feature; never compiled, instantiated or run

Each group becomes a bit mask in hex, bit i for entry i of its list: js=<hex>;intl=<hex>;wasm=<hex>. A group that cannot be read is -: no Intl, or no WebAssembly.validate (Safari Lockdown Mode, Edge’s enhanced security, V8 without a JIT). The lists only grow at the end; a longer list raises the collector’s revision.

Why it helps

Little new entropy, but it dates the engine. Each feature has a first release per engine, taken from MDN’s browser-compat-data 8.1.7, so the vector says which engine versions are possible. The version_vector_mismatch flag compares it with the engine version the user agent and the Client Hints claim, and fires when the claim is impossible: anti-detect browsers and bot frameworks often claim an engine or a version they do not run.

Stability

It changes with every browser update that adds a listed feature, and is the same between sessions in one version. Reported outside both ids. Added in library PR #33 (2026-10-27) at signalsVersion 12: it is outside both ids, so the ids did not change; the version moved because of the new flag.

Browser support and what blocks it

  • In the self-hosted CI browser matrix of PR #33 (23 browsers and channels on Linux) the value was js=ffffff;intl=ff;wasm=ffff in every browser (Chrome 156 to canary 158, Edge 156 to dev 158, Chromium, Brave 155 and 156, Opera, Firefox ESR 153 to Nightly 161, Playwright’s WebKit), except Opera beta, which runs Chromium 142: js=c1ffff.
  • A WebAssembly group of - where WebAssembly.validate is missing: Safari Lockdown Mode, Edge’s enhanced security mode, V8 without a JIT.
  • unavailable / no_api only when the window has no JavaScript built-ins at all.
  • No browser class excludes it.

Privacy

It reads whether built-in features exist, nothing about the user, and runs no code from them. No prompt, no request. Leave it out with collect({ exclude: ['engine_features'] }) if you do not need it; no id changes.

How WhiskerJS handles noise and failures

Status ruleId When
ok three bit masks, - for a group that cannot be read
unavailable no_api no JavaScript built-ins in the window
blocked threw, security_error a read threw
excluded caller_excluded you excluded it
timed_out timeout, aborted budget or abort

Example value

"engine_features": { "status": "ok", "value": "js=ffffff;intl=ff;wasm=ffff", "hashed": false, "durationMs": 1.6, "revision": 1 }

Questions and answers

Does engine_features run any WebAssembly code?

No. It calls WebAssembly.validate() on tiny modules of a few bytes each, which only checks whether the engine understands them. Nothing is compiled, instantiated or run.

Can a page polyfill change the value?

No. A function counts only when the engine's own Function.prototype.toString prints it as native code, so a polyfill written in JavaScript does not count. The version_vector_mismatch check also reads the features in a clean, hidden iframe.

Why is it not in the visitorId?

Browsers add features with almost every release, so the value changes with each update. It is reported for your server, and the version_vector_mismatch flag compares it with the version the browser claims.