Browsers · WhiskerJS 0.1.0

Firefox

Stock Firefox is read without exclusions; with Fingerprinting Protection three components are left out. A few Chromium-only APIs are missing, and WebGL strings come from the plain renderer enums.

In plain words

In a normal Firefox, WhiskerJS reads almost everything and leaves nothing out on purpose. A few measurements only exist in Chrome-like browsers, so they come back as “unavailable”. Firefox has two privacy features that change what pages see. Fingerprinting Protection (on in private windows, and rolling out to normal windows) adds noise to canvas and WebGL reads: WhiskerJS notices that with a tiny test drawing and leaves those three signals out. The stricter resistFingerprinting mode makes Firefox report fixed values; WhiskerJS only treats a Firefox as being in that mode when every sign of it is present, so an ordinary Firefox is not mistaken for it.

At a glance

  • Browser class: firefox (a Firefox/<number> token in the user agent, or mozInnerScreenX on the window); firefox_fpp when Fingerprinting Protection alters canvas or WebGL readback.
  • Excluded components: none in stock Firefox; 3 under Fingerprinting Protection (canvas, screen_frame, webgl_image, ruleId firefox_fpp).
  • Canvas id: stable across sessions since signalsVersion 9 (the pixels are read with getImageData(), which Firefox does not randomise).
  • Unavailable by design: device_memory, heap_limit, ua_client_hints, ua_versions, keyboard_layout.
  • Skipped on purpose on desktop: audio_latency.
  • Lab sample: none for Firefox yet.

What works

  • Graphics: canvas and webgl_image with the double render and the known-answer probes, webgl_params, and webgl_renderer and webgl_vendor. In Firefox, WhiskerJS does not request WEBGL_debug_renderer_info, because Firefox prints a deprecation warning when it is requested; gl.RENDERER and gl.VENDOR already carry the unmasked strings there.
  • vendor: Firefox reports an empty navigator.vendor, which WhiskerJS reports as the plain value empty.
  • Offline audio, fonts (both methods), font preferences, Math, codecs, plugins (typically a fixed PDF list), locale, storage presence, permission states and the display preference queries.
  • webgpu where the Firefox version and platform offer navigator.gpu; otherwise unavailable / no_api.

What is unavailable or blocked

Component Status Why
device_memory unavailable / no_api no navigator.deviceMemory
heap_limit unavailable / no_api no performance.memory
ua_client_hints, ua_versions unavailable / no_api no navigator.userAgentData
keyboard_layout unavailable / no_api no navigator.keyboard
audio_latency unavailable / skipped on desktop, creating an AudioContext without a user gesture prints a console warning

Firefox for Android has an Android user agent, so audio_latency is read there. apple_pay and private_click report the plain value none.

What WhiskerJS excludes and why

Nothing in stock Firefox. The firefox row of the exclusion table is empty.

Fingerprinting Protection (firefox_fpp)

Firefox Fingerprinting Protection randomises canvas and WebGL readback per site and session and collapses the available screen area to the screen. It is on in private windows and, since Firefox 151, is rolling out to normal windows, starting with canvas randomisation. WhiskerJS does not read the setting. It classes a Firefox as firefox_fpp when it is not resistFingerprinting and the canvas or WebGL known-answer probe came back altered. Then:

Component Why it is left out
canvas canvas readback is randomised per site and session
screen_frame the available screen area is collapsed to the screen
webgl_image WebGL readback is randomised per site and session

All three report excluded with ruleId firefox_fpp. Stock Firefox reads the known answer back exactly, also under automation and with a screen that has no taskbar, so it stays firefox. Audio noise alone does not make the class (the audio component then turns noisy on its own). A stock Firefox with a canvas-noise extension is also classed firefox_fpp, which is right for the exclusions: its canvas is randomised too. When Firefox turns Fingerprinting Protection on in normal windows, browser_class changes and so does the id, once.

Before signalsVersion 9, stock Firefox had a second problem: Firefox 155 randomises toDataURL() per session while getImageData() stays exact, and the canvas component hashed toDataURL(), so stock Firefox gave a new id in every new browser context. Since signalsVersion 9 canvas reads the pixels and the Firefox id holds across sessions. The previous id for this bump will rarely match a stored Firefox id, because the version 8 id was not stable there.

resistFingerprinting

Only when Firefox runs with privacy.resistFingerprinting does the firefox_rfp row apply, and the test for it is deliberately strict. Each trait alone (a UTC time zone, a screen the size of the window, a pixel ratio of 1) is common in stock Firefox, so all of them must hold at once, and a browser that reports navigator.webdriver as true is never classed firefox_rfp.

That strictness is the fix of signalsVersion 5: before it, stock Firefox under automation such as Playwright (UTC, screen equal to the viewport) was wrongly classed firefox_rfp and lost nine components. A Firefox run under Playwright in a test container, at signalsVersion 5, was classed firefox and raised only automation_webdriver; since signalsVersion 8 Playwright with an exposed function also raises exposed_binding. See What’s new and Firefox resistFingerprinting and Tor Browser.

Integrity flags to expect

  • engine_mismatch checks that a Firefox class behaves like Gecko: a 37-character eval source and navigator.productSub 20100101. A user agent claiming Firefox in another engine raises it; so can user-agent switcher extensions.
  • automation_webdriver under Selenium, Playwright or similar.
  • native_tampering, prototype_lie and navigator_own_property with privacy or anti-fingerprinting add-ons.
  • software_renderer where WebGL runs in software, for example llvmpipe on some Linux setups.
  • exposed_binding under Playwright with an exposed function. Since signalsVersion 9 it reads window properties through their descriptors, so Firefox no longer prints deprecation warnings (Window.fullScreen, InstallTrigger).
  • ua_ch_mismatch is never checked: Firefox has no navigator.userAgentData. media_query_mismatch does not run for the firefox_fpp class.
  • Fingerprinting Protection’s noise is never a flag: it shows in noise_profile, canvas_readback and the excluded components.

Early lab observations

There is no lab data for Firefox yet. The early, small sample at signalsVersion 1 covered Mac Chrome, Mac Safari, iPhone Chrome and iPhone Safari only. The table below comes from the code.

Testing it yourself

Open the live demo in Firefox and check browser_class: it should say firefox, or firefox_fpp in a private window or with Fingerprinting Protection on (then noise_profile shows which probe saw noise). Run it again after a Firefox update and see which structuredId group moved. The lab mode copies one line per run. Nothing is sent anywhere.

Support table for Firefox

Expected status of every component in this browser at signalsVersion 9, from the support matrix (updated 2026-10-26). Basis "code" means expected from the library code and documentation, not yet measured; "lab" means seen in the early lab sample.

ComponentStatusBasisNote
apple_paynonecodeNo ApplePaySession: reports the plain value none.
audiookcodenoisy / seeded_noise if the audio known-answer probe sees noise.
audio_latencyvariescodeSkipped on desktop; Firefox for Android has an Android user agent, so it is read there.
browser_classokcodeClass firefox; firefox_fpp when Fingerprinting Protection alters canvas or WebGL readback.
canvasvariescodeExcluded (firefox_fpp) when Fingerprinting Protection is on: in private windows, and in normal windows as Firefox 151 and later roll it out. Otherwise ok.
canvas_readbackvariescodeexact, or altered with Fingerprinting Protection or a canvas-noise extension.
color_depthokcode
color_gamutokcode
content_blockersokcodenone, or the bait names a content blocker hid.
cpu_architectureokcode
device_memoryunavailablecodenavigator.deviceMemory is a Chromium API. Reports unavailable / no_api.
dynamic_rangeokcode
font_preferencesokcode
fontsokcode
fonts_canvasokcode
forced_colorsokcode
hardware_concurrencyokcodeRead 16 times; noisy / read_varies if the reads disagree. Some browsers cap or round the value.
heap_limitunavailablecodeperformance.memory is a Chromium API. Reports unavailable / no_api.
intl_localeokcode
keyboard_layoutunavailablecodeNo navigator.keyboard here: unavailable / no_api.
languagesokcode
mathokcode
media_codecsokcode
noise_profileokcodeexact on stock Firefox; seeded for canvas or WebGL with Fingerprinting Protection or a noise extension.
pdf_viewerokcode
permissionsokcode
pixel_ratiookcode
platformokcode
pluginsokcode
prefers_contrastokcode
private_clicknonecodeNo attributionSourceId on anchors: reports the plain value none.
reduced_motionokcode
screen_framevariescodeExcluded (firefox_fpp) when Fingerprinting Protection is on: in private windows, and in normal windows as Firefox 151 and later roll it out. Otherwise ok.
screen_resolutionokcode
speech_voicesokcodeDigest of the voice list, or none; some browsers fill the list late.
storageokcode
time_zoneokcode
touch_supportokcode
ua_client_hintsunavailablecodenavigator.userAgentData (Client Hints) is a Chromium API. Reports unavailable / no_api.
ua_versionsunavailablecodenavigator.userAgentData (Client Hints) is a Chromium API. Reports unavailable / no_api.
vendorokcode
vendor_flavorsokcode
webgl_imagevariescodeExcluded (firefox_fpp) when Fingerprinting Protection is on: in private windows, and in normal windows as Firefox 151 and later roll it out. Otherwise ok.
webgl_paramsokcodeWhere WebGL 1 is available; unavailable / no_context without it.
webgl_rendererokcodeRead from gl.RENDERER; WEBGL_debug_renderer_info is not requested in Firefox.
webgl_vendorokcodeRead from gl.VENDOR; WEBGL_debug_renderer_info is not requested in Firefox.
webgpuvariescodeDepends on browser version, operating system and GPU; needs a secure context. unavailable / no_api without navigator.gpu, no_context without an adapter.

Questions and answers

Why does WhiskerJS not request WEBGL_debug_renderer_info in Firefox?

Firefox prints a deprecation warning in the console when that extension is requested, and its plain gl.RENDERER and gl.VENDOR values already carry the unmasked strings. WhiskerJS writes nothing to the console, so it reads the plain values.

My Firefox runs in UTC. Will it be treated as resistFingerprinting?

Not on that alone. The firefox_rfp class needs every trait at once: an RFP time zone, a screen equal to the content area, no taskbar space, pixel ratio 1, a window at 0,0, and navigator.webdriver not true.

Why did my Firefox get a new id in every new session before signalsVersion 9?

Firefox 155 randomises canvas toDataURL() per session while getImageData() stays exact. Until signalsVersion 8 the canvas component hashed toDataURL(), so stock Firefox gave a new id in every new browser context. Since signalsVersion 9 canvas reads the pixels with getImageData(), and the Firefox id is stable across sessions.

What is the firefox_fpp class?

Firefox with Fingerprinting Protection: a Firefox that is not resistFingerprinting and whose canvas or WebGL known-answer probe came back altered. FPP is on in private windows and, since Firefox 151, is rolling out to normal windows. canvas, screen_frame and webgl_image are then excluded with ruleId firefox_fpp.

Is there lab data for Firefox?

No. The early lab sample covered Mac Chrome, Mac Safari, iPhone Chrome and iPhone Safari only.