# noise_profile

> Three known-answer probes, each read twice: canvas, WebGL and audio are exact, seeded, per_read or na. A browser trait, not an integrity flag. Not in either id.

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

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

| Field | Value |
| --- | --- |
| Component name | `noise_profile` |
| In the visitorId | no (reported, kept out of both ids) |
| structuredId group | none |
| 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 6](https://whiskerjs.com/whats-new/) |

> **In plain words:**
>
> Some browsers add tiny random changes to what a page reads back from a canvas, a 3D drawing or a sound, so that those reads cannot be used to recognise the device. WhiskerJS draws three things whose exact result is known in advance (two colours on a canvas, one colour in WebGL, a constant level of sound), reads each twice and compares. The result says, for each of the three, whether the browser returned the exact answer, the same wrong answer twice, or a different answer each time. It describes the browser’s privacy settings, so it is kept out of the identifiers and is never treated as a sign of tampering.

## What it reads

Three known-answer probes, each read twice. The value has the form `canvas=<state>,webgl=<state>,audio=<state>`:

- **canvas**: a 16x16 2D canvas, left half `rgb(17, 34, 51)` and right half `rgb(204, 153, 102)`, read with `getImageData()`. Every pixel must come back with exactly those bytes and alpha 255.
- **webgl**: the collection’s shared 32x32 WebGL drawing buffer, cleared to `(51, 102, 153, 255)` and read with `readPixels()`. Every pixel must come back with those four bytes.
- **audio**: an `OfflineAudioContext` (1 channel, 1024 frames at 44100 Hz) rendering a `ConstantSourceNode` at 0.5, read with `getChannelData(0)`. Every sample must be exactly 0.5. Nothing is played and no audio device is opened.

## How the technique works

1. Each probe produces its known answer and reads it back twice.
2. Both reads exact gives `exact`. The same wrong answer twice gives `seeded`. Two different answers give `per_read`. A probe that cannot run (no canvas, no WebGL, no offline audio, or an error) gives `na`.
3. The canvas and WebGL probes run once per collection, before the collectors, because their result picks the `firefox_fpp` and `safari_afp` [browser classes](https://whiskerjs.com/signals/browser-class/). The audio probe runs beside the collectors; `noise_profile` waits at most 800 ms for it and reports `audio=na` after that.
4. The same results are used by [`canvas`](https://whiskerjs.com/signals/canvas/), [`webgl_image`](https://whiskerjs.com/signals/webgl-image/) and [`audio`](https://whiskerjs.com/signals/audio/): when their own render looks clean but the probe of the same API is `seeded` or `per_read`, they report `noisy` with ruleId `seeded_noise`.

The probes run for every collection except an aborted one, also when you exclude the components that use them.

## Why it helps

Seeded noise is the same on every read within a session, so drawing the same scene twice cannot see it; only a comparison with a known answer can. Reporting which probes failed tells your server that a browser is privacy-hardened (Brave, Firefox Fingerprinting Protection, Safari Advanced Fingerprinting Protection, or a noise extension) and why some graphics or audio components are `noisy`.

## Why it is not in the id

It describes the browser’s privacy settings and extensions, which users change. It is a browser trait, not an [integrity flag](https://whiskerjs.com/docs/integrity/): noise is a privacy choice, not tampering. Changing it never changes the visitorId or the structuredId.

## Stability

Stable for a given browser and its settings. It changes when a protection mode or a noise extension is switched on or off, when a private window is opened (several browsers add noise only there), and `audio` can move between a state and `na` on a slow or busy device that misses the 800 ms wait.

## Browser support and what blocks it

- Stock [Chrome](https://whiskerjs.com/browsers/chrome-desktop/), [Edge](https://whiskerjs.com/browsers/edge/), [Firefox](https://whiskerjs.com/browsers/firefox/) and [Safari](https://whiskerjs.com/browsers/safari-macos/): `canvas=exact,webgl=exact,audio=exact` where WebGL and offline audio exist.
- [Brave](https://whiskerjs.com/browsers/brave/), [Firefox with Fingerprinting Protection](https://whiskerjs.com/browsers/firefox/) and [Safari with Advanced Fingerprinting Protection](https://whiskerjs.com/browsers/safari-macos/): typically `seeded` for the APIs they randomise.
- Without WebGL (turned off, blocklisted GPU, some containers): `webgl=na`.
- The component itself is always `ok`; it is never excluded by a browser class.

## Privacy

It reveals only whether the three reads are altered, which suggests a privacy setting or an extension. The test patterns are fixed and contain nothing about the page or the user. WhiskerJS does not try to work around the noise.

## How WhiskerJS handles noise and failures

| Status | ruleId | When |
| --- | --- | --- |
| `ok` |   | the three probe states, for example `canvas=exact,webgl=na,audio=exact` |
| `excluded` | `caller_excluded` | you excluded it (the probes still run for the class and the other components) |
| `timed_out` | `timeout`, `aborted` | the 1000 ms inner timeout, the overall budget or your abort signal |

A probe that fails reports `na` inside the value instead of a non-ok status. Never hashed.

## Example value

A stock browser with WebGL and Web Audio:

```json
"noise_profile": { "status": "ok", "value": "canvas=exact,webgl=exact,audio=exact" }
```

## Questions and answers

### What do exact, seeded, per_read and na mean?

exact: both reads gave the known answer. seeded: both reads gave the same wrong answer, which is noise seeded per session or per site (Brave, Firefox Fingerprinting Protection, Safari Advanced Fingerprinting Protection). per_read: the two reads differed, noise drawn on every read. na: the probe could not run here; audio is also na when its probe takes longer than 800 ms.

### Is seeded noise a sign of fraud?

No. Noise is a privacy choice made by the browser or the user, not tampering, so it is reported here and never as an integrity flag. Read it as a trait of the browser's settings.

### Why is noise_profile not in the visitorId?

It follows privacy settings and extensions, which users switch on and off. The components the noise affects (canvas, webgl_image, audio) turn noisy instead, which keeps the id the same from session to session.

## Related pages

- [canvas_readback](https://whiskerjs.com/signals/canvas-readback/): The canvas probe alone, as exact or altered.
- [canvas](https://whiskerjs.com/signals/canvas/): Turns noisy with seeded_noise when the canvas probe fails.
- [webgl_image](https://whiskerjs.com/signals/webgl-image/): Turns noisy with seeded_noise when the WebGL probe fails.
- [audio](https://whiskerjs.com/signals/audio/): Turns noisy with seeded_noise when the audio probe fails.
- [browser_class](https://whiskerjs.com/signals/browser-class/): The canvas and WebGL probes pick the firefox_fpp and safari_afp classes.
- [Noise handling](https://whiskerjs.com/docs/how-it-works/#noise-handling-the-double-render-and-the-known-answers): The double render and the known answers in the docs.
