Signal

JS heap size limit

Reads Chromium's V8 heap ceiling, a device-class tier indicator that's also reduced in incognito mode. Chromium-only; absent on Safari and Firefox.

Reviewed

Tier 1 engine

What it measures

performance.memory.jsHeapSizeLimit is the V8 heap ceiling Chromium chose for the current tab at startup. Typical observed values: low-RAM Android (1 to 2 GiB device RAM) reports 256 to 512 MiB; mid-range desktop / Chromebook (4 to 8 GiB RAM) reports ~2 GiB; high-RAM desktop (16+ GiB RAM) reports ~4 GiB. Chromium caps the limit at 4 GiB on 64-bit regardless of physical RAM.

Chromium incognito mode reduces the cap below the normal-mode value, the same observation the existing incognito-detection layer uses via the flag_js_heap_limit_low flag (which fires when limit < 3 GiB).

Why engine-bound

Only Chromium exposes performance.memory. Safari and Firefox return undefined. Brave inherits Chromium and exposes the same value without farbling.

Including this in the hardware signal set would shatter cross-browser stability. Chrome on a Mac would give a value while Safari on the same Mac would give absent. Classifying it engine-bound keeps hardwareFingerprint clean.

Confidence rules

ConfidenceTrigger
normalperformance.memory.jsHeapSizeLimit is a positive finite number
absentperformance.memory missing (Safari / Firefox)
absentjsHeapSizeLimit is undefined, null, non-number, or ≤ 0

Things worth knowing

  • The existing flag_js_heap_limit_low incognito-detection flag reads the same API independently. Adding this signal does not change that flag's logic. Both coexist without interference.
  • Brave Standard Shields does NOT farble performance.memory. Values match upstream Chromium.
  • performance.memory is non-standard and has been on Chromium's 'consider removing' list for years. If removed, this signal becomes permanent absent across all browsers, a graceful failure mode.
  • FingerprintJS does not collect jsHeapSizeLimit as a hash-contributing signal (only for confidence scoring). Thumbmark collects it inside its hardware component. We sit in between, collecting it as an engine-bound standalone signal in 1.7.0.

Last reviewed 2026-06-04