# device_memory

> Approximate device memory in GB, in coarse buckets. Offered only by Chromium-based browsers.

Source: https://whiskerjs.com/signals/device-memory/ (last updated 2026-10-26)

**device_memory at a glance (from the library code, signalsVersion 9)**

| Field | Value |
| --- | --- |
| Component name | `device_memory` |
| In the visitorId | yes |
| structuredId group | [device (screen, memory, CPU)](https://whiskerjs.com/docs/identifiers/#structuredid) |
| Hashing | plain value; a digest only when longer than 128 characters or not printable ASCII |
| Excluded under | no browser class |
| Inner timeout | 1000 ms |
| Added in | [signalsVersion 1](https://whiskerjs.com/whats-new/) |

> **In plain words:**
>
> Chrome and other Chromium-based browsers tell pages roughly how much memory the device has, rounded to a few fixed steps such as 4 or 8 GB, so sites can serve lighter pages to small devices. Safari, every browser on iPhone and iPad, and Firefox do not offer this. WhiskerJS reads it where it exists and reports “unavailable” elsewhere, which is expected.

## What it reads

`navigator.deviceMemory`, read 16 times in a row (since signalsVersion 7), reported as a plain number when it is a finite number and every read agrees. On Chromium 146 and later (a V8 browser class whose user agent carries `Chrome/` or `Chromium/` 146 or higher) the value carries the tag `;c146`.

## How the technique works

The browser rounds the real memory to a coarse value before exposing it, so pages only learn a bucket. WhiskerJS reads the property; a missing property gives `unavailable` / `no_api`, a throwing getter `blocked`. The value is read 16 times: when the reads disagree the component is `noisy` / `read_varies` and carries no value, so the id does not flap, and the value is left out of the [`iframe_mismatch`](https://whiskerjs.com/docs/integrity/#iframe_mismatch) and [`prototype_lie`](https://whiskerjs.com/docs/integrity/#prototype_lie) cross-checks.

Chromium 146 changed the bucket set: 2, 4 or 8 GB on Android, and up to 32 GB elsewhere, where the old cap was 8. From that version the value carries the tag `;c146` (for example `16;c146`), so a server can tell which scheme a value comes from. For the [previous ids](https://whiskerjs.com/docs/identifiers/#previous-ids-linking-across-a-version-bump) of the signalsVersion 6 to 7 bump the first read was recorded without the tag.

## Why it helps

The bucket separates device classes (small phones, mid-range laptops, large desktops) and combines with CPU threads, architecture and the heap limit in the `device` group. Its absence also carries information: an `unavailable` value next to a Chrome user agent is unusual and worth a look on your server.

## Stability

Fixed for a given device and browser. It changes on a different device, or when a browser changes its bucketing: expect one id change per Chromium user at the update to Chromium 146 (the value gains `;c146` and may move to a new bucket), or at signalsVersion 7 for browsers already on it.

## Browser support and what blocks it

- `ok` in Chromium-based browsers: [Chrome desktop](https://whiskerjs.com/browsers/chrome-desktop/), [Chrome on Android](https://whiskerjs.com/browsers/chrome-android/), [Edge](https://whiskerjs.com/browsers/edge/), [Brave](https://whiskerjs.com/browsers/brave/) (not in Brave’s exclusion row), [Samsung Internet](https://whiskerjs.com/browsers/samsung-internet/).
- `unavailable` / `no_api` by design in [Safari on macOS](https://whiskerjs.com/browsers/safari-macos/), [every iOS browser](https://whiskerjs.com/browsers/safari-ios/) and [Firefox](https://whiskerjs.com/browsers/firefox/).

The early lab sample, an early, small sample at signalsVersion 1, not a benchmark, matched this: `device_memory` was `unavailable` on Mac Safari, iPhone Safari and iPhone Chrome (which runs on WebKit), and each of the four device-browser pairs still kept one id across its runs.

Related checks: [`impossible_hardware`](https://whiskerjs.com/docs/integrity/#impossible_hardware) fires when the value is a number but not a power of two from 0.25 to 64, [`navigator_own_property`](https://whiskerjs.com/docs/integrity/#navigator_own_property) and [`iframe_mismatch`](https://whiskerjs.com/docs/integrity/#iframe_mismatch) look at the same property.

## Privacy

A coarse bucket chosen by the browser to limit what pages learn. Read without permission or prompt, never sent by WhiskerJS.

## How WhiskerJS handles noise and failures

| Status | ruleId | When |
| --- | --- | --- |
| `ok` | none | for example `8`, or `16;c146` on Chromium 146 and later |
| `noisy` | `read_varies` | the 16 reads did not all agree |
| `unavailable` | `no_api` | not offered (Safari, iOS, Firefox) or not a number |
| `blocked` | `security_error`, `threw` | the getter threw |
| `excluded` | `caller_excluded` | your `exclude` list |
| `timed_out` | `timeout`, `aborted` | 1000 ms inner timeout, budget, abort |

## Example value

```json
"device_memory": { "status": "ok", "value": "8", "hashed": false }
```

On Chromium 146 and later: `{ "status": "ok", "value": "16;c146", "hashed": false }`.

In Safari and Firefox: `{ "status": "unavailable", "ruleId": "no_api" }`.

## Questions and answers

### Why is device_memory unavailable on my iPhone or in Safari?

Only Chromium-based browsers offer navigator.deviceMemory. Safari, every iOS browser (they all run on WebKit) and Firefox do not, so the component is unavailable with ruleId no_api by design.

### Does an unavailable device_memory change the visitorId?

Not from run to run. Every non-ok status adds the same placeholder line, so a browser that never offers the API gives a stable id.

### Can device_memory report my exact RAM?

No. Browsers round it to a coarse bucket, such as 4 or 8, on purpose.

### What does the ;c146 at the end of the value mean?

Chromium 146 changed the memory buckets (2, 4 or 8 on Android; up to 32 elsewhere, where the old cap was 8). From that version WhiskerJS adds the tag ;c146, for example 16;c146, so a server can tell which scheme a value comes from. Expect one id change per Chromium user at the update to 146, or at signalsVersion 7 for browsers already on it.

## Related pages

- [heap_limit](https://whiskerjs.com/signals/heap-limit/): The JavaScript heap limit, also Chromium only.
- [hardware_concurrency](https://whiskerjs.com/signals/hardware-concurrency/): CPU threads.
- [impossible_hardware flag](https://whiskerjs.com/docs/integrity/#impossible_hardware): Catches memory values no browser reports.
- [Safari and every iOS browser](https://whiskerjs.com/browsers/safari-ios/): Where it is unavailable.
- [FAQ](https://whiskerjs.com/docs/faq/): Why device_memory is unavailable in Safari.
