How Browser Fingerprinting Techniques Work

How canvas, font, WebGL, AudioContext and Client Hints fingerprinting work, what each one reveals about your device and how browsers block or change each signal.

How Browser Fingerprinting Techniques Work

A fingerprinting script does not need one perfect identifier. It collects several signals that each narrow the field a little, hashes them together and recognises you by the combination. The techniques on this page are the high-entropy ones: canvas rendering, installed fonts, WebGL and AudioContext output, and the Client Hints a Chromium browser sends. Each reads a different layer of your machine, which is why blocking one leaves the others working.

Canvas and font probing both use the 2D canvas, one by reading back the pixels of a hidden drawing and the other by measuring text widths, so they reveal your graphics stack, operating system and installed software. WebGL exposes the GPU and its driver, and AudioContext exposes how your system processes a silent test tone. Client Hints work differently: they are values the browser reports on request, and how much they reveal depends on which hints a server asks for. Each section explains how the technique works, what it reveals and how Brave, Firefox, Safari and Tor change it, so you can compare the matching row in the inspector before and after a change. Client Hints have no row of their own; the Client Hints section shows how to read them in your browser's developer tools.

Rows to compare in the inspector

  • Canvas hash changes between browsers on the same machine; a hash that changes on every reload means noise is being added
  • Installed fonts reflects your operating system, locale and installed software
  • WebGL renderer an empty or generic value means the browser blocks or spoofs it
  • Audio fingerprint comes from how your system processes a silent test tone

Opens the Browser Fingerprint Inspector with this page's checklist shown at the top of the tool.

Open in the tool →

Check Your Canvas Fingerprint Hash

Run the inspector above and it generates your browser's own canvas hash the same way any tracking script would: by exploiting the HTML5 canvas element to render text and graphics, then reading back the pixel data.1 Because rendering varies by hardware, driver, and browser, the resulting hash stays highly unique and persistent across sessions, which is what makes it effective for tracking.2

Research timeline

  • 2012, Mowery and Shacham, UCSD
  • 2014, EFF
  • 2016, found on 14,000+ of top 100,000 sites

Opens the Browser Fingerprint Inspector with this section's reference values shown at the top of the tool.

Open in the tool →

Why the hidden canvas is so stable

Drawing a hidden canvas produces a stable signal because the browser repeats the same rendering instructions each time, and the underlying hardware and software stack that executes those instructions does not change between visits. A script carefully chooses specific fonts, text strings, colors, gradients, and geometric shapes, then asks the browser to export the resulting pixel buffer through the CanvasRenderingContext2D API.2

Why hardware determines the output

Your GPU, driver, operating system compositor, and font rasterizer influence the result before the script ever hashes it, because each of these layers applies its own anti-aliasing and sub-pixel rendering decisions to the same drawing commands. Consequently, the same machine tends to produce the same canvas hash across page loads, since the rendering stack does not change between visits. This stability is what makes the technique attractive for tracking, and it explains why even two machines with the same browser version can produce different hashes when their GPU drivers differ.

You can see this stability for yourself by running the same page twice in CapyToolkit and comparing the canvas hash it reports each time. The value stays the same on a given device because the rendering stack does not change between loads, which is exactly why trackers rely on it. Changing your GPU driver or switching browsers is usually what shifts the hash, not a routine software update or a cleared cache.3

Testing the canvas hash in CapyToolkit makes this stability observable instead of abstract. The inspector reports the exact hash each run, so you can watch it stay fixed on one device and change the moment the rendering stack differs. That repeatable value is the whole reason the technique works as a tracking identifier rather than a random number.

What makes canvas different from ordinary cookies

Canvas fingerprinting does not need to store a value before it can recognize you. The script reads the rendering result immediately, hashes it, and sends that hash to its server. Because the calculation happens during the page visit, deleting site data after the fact does not prove that the signal was never collected.

Browsers cannot simply make every GPU render the same pixels without breaking graphics applications that depend on accurate color output and pixel-perfect rendering. A small change such as adding minor noise can help privacy, but a large change can distort charts, images, and games in ways that users notice immediately. That is why the best protections add controlled noise or detect suspicious hidden-canvas access rather than disabling the entire Canvas API for everyone, trading a small amount of rendering accuracy for a meaningful reduction in fingerprinting entropy.

How blocking changes the signal

Blocking canvas fingerprinting changes the signal at different depths. A browser extension can add noise to the exported pixels, which makes the hash change on each page load.4 Brave uses per-origin farbling so the value remains usable for the current site but does not link cleanly to another site.5 Firefox with privacy.resistFingerprinting can modify fingerprinting-related browser data at the engine level.6 Tor Browser can return a blank or null result at higher security levels.7 These approaches reduce uniqueness, but they also create different compatibility trade-offs. The key distinction is between noise injection, which preserves per-site functionality while preventing cross-site linking, and total blocking, which eliminates the signal entirely but can disrupt applications that depend on canvas for rendering charts, processing images, or generating data URIs.

When canvas blocking can break legitimate features

When you block canvas access completely, you may also block legitimate features that depend on the same rendering pipeline for essential functionality. Image editors, chart libraries, CAPTCHA systems, and data URI exports can all rely on canvas rendering to generate visible output or process user-submitted content.1 A broad block can therefore create broken buttons, failed verification challenges, missing image previews, and charts that fail to render, which is why a more targeted approach is almost always preferable to a blanket API suppression.

How to target blocking more precisely

A more practical approach often targets suspicious behavior rather than blocking the entire canvas API. Scripts that create hidden canvases, export pixel data immediately after drawing, and produce no visible user-facing output are far more likely to be fingerprinting than legitimate application code. This behavioral distinction lets you reduce tracking while preserving tools that need canvas for real work such as image editors, chart libraries, and CAPTCHA systems that rely on visible canvas output.4 Firefox with privacy.resistFingerprinting takes a middle path by modifying the pixel buffer at the rendering engine level rather than blocking the API, which keeps canvas-dependent applications functional while still degrading the fingerprinting signal that scripts can extract.

For testing your own exposure

For testing your own exposure, compare the canvas row before and after changing browsers or settings so you can see the concrete effect of each modification. Open CapyToolkit in your default browser and record the canvas hash, then repeat the test in Brave with Shields active, Firefox with privacy.resistFingerprinting, or Tor Browser to observe how each alternative changes the output.

If the value changes, disappears, or becomes marked as blocked, the browser is reducing the canvas surface. If the value stays identical, your current setup still exposes a stable canvas fingerprint. The test turns an abstract privacy concept into a visible result you can verify. Run the same check after installing an extension or changing a browser flag, and you will see exactly which protections are active and which signals remain exposed. That concrete feedback loop is what makes the difference between assuming you are protected and knowing it.

The research history that put canvas fingerprinting on the map

Canvas fingerprinting was first documented publicly in a 2012 paper by Mowery and Shacham at UCSD, which showed that GPU-rendered canvas output varied enough across machines to serve as a passive identifier. The technique moved from academic research to widespread deployment by 2014, when EFF researchers discovered that the White House website's analytics vendor used canvas fingerprinting without disclosure, a finding that prompted regulatory attention to the practice. By 2016, the Princeton WebTAP study found canvas fingerprinting scripts on more than 14,000 of the top 100,000 websites.

The spread was rapid because the technique required no special permissions, produced no visible output, and survived all standard privacy measures of the time: cookie deletion, private browsing, and VPN use. Ad networks adopted it as a supplement to cookie-based tracking after Apple began restricting third-party cookies in Safari; fingerprinting provided continuity where cookies could no longer persist.

Despite more than a decade of awareness, canvas fingerprinting remains effective because the underlying mechanism, GPU-rendered pixel variance, cannot be eliminated without either degrading graphics functionality or accepting a clearly detectable modification. Browsers that add noise to canvas output are detectable by their inconsistency. Browsers that return blank canvases are detectable by their nullness. Only approaches that normalize output across a large user population, such as Tor Browser's shared canvas profile, genuinely reduce tracking effectiveness rather than just changing the form of the signal.

Why canvas hashes differ across browsers and operating systems

The same machine running Chrome and Firefox produces different canvas hashes because the two browsers use different 2D rendering backends. On Windows, Chrome routes canvas rendering through ANGLE (Almost Native Graphics Layer Engine), which translates OpenGL ES calls to Direct3D. Firefox uses its own Skia-based pipeline without the ANGLE translation layer. Each backend applies different anti-aliasing algorithms to curved text edges and gradient transitions, producing different pixel values for the same drawing commands.

Operating systems introduce additional variation. The macOS Core Graphics compositor applies sub-pixel font rendering rules specific to Apple's display stack. Linux systems vary depending on which font rendering library (FreeType, cairo) and which graphics stack (X11, Wayland) the browser uses. Consequently, two machines with identical CPU and GPU hardware will produce different canvas hashes if one runs Windows Chrome and the other runs macOS Safari, because the rendering stack between JavaScript and the pixel buffer differs entirely.

How rendering differences affect fingerprint stability across updates

Browser updates that change the rendering backend, upgrade the ANGLE version, or modify anti-aliasing behavior can shift canvas hashes for an entire browser generation simultaneously. When Chrome updated its ANGLE version in 2022, canvas hashes changed for a large portion of Windows users, temporarily reducing the reliability of canvas-based re-identification. Trackers that maintain long-term fingerprint databases must account for these shifts by storing multiple historical hash values per device and accepting a brief gap in recognition after major browser releases.

When to use this

Use this guide to understand why canvas fingerprinting is so effective and how blocking or randomizing canvas output reduces your tracking surface.

Examples

How a canvas fingerprint is generated

Before
A script creates a hidden canvas, draws text and shapes with specific settings, then reads the pixel data to produce a hash.
After
The script draws "Browser Fingerprint" in 14px Arial, applies a gradient, and exports the pixel buffer. Your GPU renders it slightly differently than anyone else's, producing hash "c84efb8e...".

The hash is stable for your browser on your hardware but different on other machines, even with identical software.

Canvas fingerprint before and after blocking

Before
Websites read a consistent unique hash: c84efb8e3a92d710fbcae2f5b0078efb
After
With CanvasBlocker extension active, the hash changes on every page load: 7a1df3c94... then b902e8af1..., making tracking impossible.
Sources
  1. 1.

    Mozilla Developer Network, "Canvas API," developer.mozilla.org, July 2025. https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API

  2. 2.

    Kurt Opsahl and Peter Eckersley, "White House Website Includes Unique Non-Cookie Tracker, Conflicts With Privacy Policy," eff.org, July 2014. https://www.eff.org/deeplinks/2014/07/white-house-website-includes-unique-non-cookie-tracker-despite-privacy-policy

  3. 3.

    Electronic Frontier Foundation, "Cover Your Tracks | About," coveryourtracks.eff.org, accessed June 2026. https://coveryourtracks.eff.org/about

  4. 4.

    kkapsner, "CanvasBlocker," github.com, accessed June 2026. https://github.com/kkapsner/CanvasBlocker/blob/master/README.md

  5. 5.

    Brave, "Fingerprinting defenses 2.0," brave.com, accessed June 2026. https://brave.com/privacy-updates/4-fingerprinting-defenses-2.0/

  6. 6.

    Mozilla Developer Network, "privacy.websites," developer.mozilla.org, July 2025. https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/privacy/websites

  7. 7.

    Arthur Edelstein, "fixup! Bug #6253: return white placeholder image data," github.com, accessed June 2026. https://github.com/arthuredelstein/tor-browser/commit/7e1d5a0743e781d330a20f304d1387588468497e

FAQ

It is a tracking technique that uses the HTML5 canvas element to draw hidden graphics and then extracts a hash from the pixel data. The hash is unique to your combination of hardware, GPU drivers, browser, and OS.

It relies on low-level rendering differences in your GPU, graphics drivers, and browser engine. These subtle variations are nearly impossible to spoof without actually changing your rendering stack.

Use extensions like CanvasBlocker or CanvasFingerprintDefender, or enable Firefox's privacy.resistFingerprinting setting which returns a blank canvas. Brave blocks canvas fingerprinting by default.

Rarely. Canvas fingerprinting is mostly used for tracking, not for essential functionality. Some CAPTCHA systems may fail if the canvas is fully blocked; in that case, temporarily disable the extension.

Yes. Tools like CapyToolkit's Browser Fingerprint Inspector show when a page attempts canvas access. Developer Tools can also log canvas API calls.

Font Fingerprinting: How Your Fonts Identify You

For privacy-sensitive pages, font fingerprinting detects which fonts are installed on your system. A script renders a test string in a probe font using CanvasRenderingContext2D.measureText() and compares the measured pixel width to the fallback font width. When the measured width matches the fallback, the probe font is absent; when the width differs, the probe font is installed. This detection method runs silently in any browser context and requires no canvas image export. The W3C fingerprinting guidance specifically cites font enumeration as a high-risk fingerprinting vector because the installed font set reflects the OS, locale, installed software portfolio, and even professional specialization of the user.1 Consequently, an Illustrator user has Adobe Fonts; a developer has JetBrains Mono and Fira Code; a gamer may have specialized peripheral vendor fonts.

Building on this, a script probing 200 fonts can enumerate the installed set completely in under 100 milliseconds, making it one of the fastest high-entropy fingerprinting techniques available.2

What to look for

  • under 100ms
  • under 30ms
  • 30 to 50, per W3C guidance

Opens the Browser Fingerprint Inspector with this section's reference values shown at the top of the tool.

Open in the tool →

How canvas-based font probing works

Font probe detection uses CanvasRenderingContext2D.measureText() rather than canvas pixel export. The script creates a canvas, obtains a 2D context, sets a specific font size, and renders a test string like "mmmmmmmmmmlli" in each probe font.3 It calls measureText() on the test string and records the returned pixel width. The same test string in the fallback font (typically "monospace" or "sans-serif") has a known baseline width. When the probe font is installed, the measured width differs from the baseline because the font uses different glyph widths.

Why font probing is hard to notice

When the probe font is absent, the browser falls back to the default font and the measured width matches the baseline. Consequently, no canvas pixel buffer is exported; measureText() returns a number, not an image, which means canvas image blocking does not prevent font detection. Building on this, a script probing 100 fonts requires only 100 measureText() calls, completing in under 30 milliseconds on most hardware without triggering any visible browser behavior.

The speed is what makes font probing dangerous rather than merely possible. Each individual measurement finishes in a fraction of a millisecond, so a full scan of hundreds of fonts adds no perceptible delay to page load. You can watch the effect yourself in the font section of the fingerprint inspector, where the complete enumerated set appears instantly even though the script tested far more typefaces than a human could read.4

Why font sets identify OS, locale, and installed software

The installed font set is a deterministic function of three factors: the OS default installation, the locale settings, and the installed application portfolio. Windows 11 installs a specific set of Microsoft fonts absent from macOS. macOS installs a different set of Apple-licensed fonts, including several typefaces not present on Windows.5 Linux distributions vary widely, with some shipping only open-source fonts and others including commercial fonts under OEM licenses.

Beyond the OS defaults, locale settings add language-specific fonts: East Asian language packs include CJK typefaces not present in Western locale installations. Furthermore, application installations predictably add specific fonts: Adobe Creative Cloud adds Adobe Fonts, Microsoft 365 adds additional Office fonts, JetBrains IDE installs JetBrains Mono, and some gaming peripherals install vendor-specific display fonts. Building on this, the W3C fingerprinting guidance cites font enumeration as capable of identifying users across sites because the combination of OS default fonts plus application-specific fonts creates a distinctive profile that changes infrequently.1

Why font lists change so slowly

Installed fonts rarely change during a normal browsing session, so a font list remains stable enough for cross-session recognition. This stability is why browser-level font limiting is more effective than clearing cookies or site data. Unlike cookies, which users clear regularly, the font set persists across browser restarts, VPN changes, and even operating system updates, making it one of the most durable passive identifiers available to fingerprinting scripts. A font profile recorded during a first visit can re-identify the same browser months later with high confidence, even when all other session data has been reset.6

Blocking font fingerprinting without breaking text rendering

Blocking font fingerprinting requires restricting the font list that browsers report through canvas measurement, not blocking canvas image exports. Brave with Standard Shields active limits the font probing response to a common baseline subset, making all probe fonts outside the allowed list appear absent regardless of whether they are actually installed.7 Checking which fonts your browser exposes through measureText() reveals whether your Brave protection is actually working, since a protected browser returns a short consistent list rather than a detailed enumeration of every typeface you have installed.

How protections change font visibility

Firefox with privacy.resistFingerprinting enabled restricts font access through a similar mechanism, reporting a standardized font set rather than the actual installed set.8 Conversely, blocking canvas access entirely prevents measureText() from returning meaningful values but breaks font-dependent text layout applications including some CAPTCHA systems, online document editors, and text-rendering tools.8 The Brave approach provides the strongest protection with the smallest compatibility impact because it restricts the probe response rather than the API itself.

When to use this

Use this guide when investigating why the Installed Fonts section of CapyToolkit's Browser Fingerprint Inspector shows a distinctive result, or when evaluating whether your font set contributes significantly to your overall entropy score.

Examples

Font probe detection technique

Before
A script sets ctx.font = "72px monospace" and measures "mmmmmmmmmmlli", baseline width = 320px
After
The script then sets ctx.font = "72px 'Calibri', monospace" and measures again. If width differs from 320px, Calibri is installed. If identical, Calibri falls back to monospace, absent.

This single comparison runs in under 0.1ms. Repeating it for 200 fonts takes less than 30ms total, invisible in page load timing.

Font set in Brave vs. Chrome on the same Windows machine

Before
Chrome: full font list detected: Segoe UI, Calibri, Cambria, Adobe Fonts (if CC installed), JetBrains Mono (if IDE installed), etc.
After
Brave with Shields: reduced common subset returned: Segoe UI and common system fonts appear; application-specific fonts like Adobe Fonts and JetBrains Mono return as absent regardless of actual installation
Sources
  1. 1.

    W3C, "Mitigating Browser Fingerprinting in Web Specifications," w3.org, 2024. https://www.w3.org/TR/fingerprinting-guidance/

  2. 2.

    BrowserLeaks, "Font Fingerprinting," browserleaks.io, accessed July 2026. https://browserleaks.io/fingerprints/fonts

  3. 3.

    whatsmy.fyi, "What Is Font Fingerprinting? How Installed Fonts Track You Online," whatsmy.fyi, accessed October 2026. https://whatsmy.fyi/blog/what-is-font-fingerprinting

  4. 4.

    BotBrowser, "Text Measurement Fingerprinting: Sub-Pixel Tracking," botbrowser.io, 2024. https://botbrowser.io/en/blog/text-measurement-fingerprinting/

  5. 5.

    9to5Mac, "iOS 26 Will Counter One of the Web's Most Invasive Tracking Methods," 9to5mac.com, July 2025. https://9to5mac.com/2025/07/29/with-ios-26-safari-will-counter-one-of-the-webs-most-invasive-tracking-methods/

  6. 6.

    Mozilla Bugzilla, "Bug 1653987 — Restrict CSS font visibility to standard fonts only when privacy.resistFingerprinting is true," bugzilla.mozilla.org, 2021. https://bugzilla.mozilla.org/show_bug.cgi?id=1653987

  7. 7.

    Brave, "GitHub Issue #51331 — Font Fingerprinting Protection (2025)," github.com/brave/brave-browser, 2025. https://github.com/brave/brave-browser/issues/51331

  8. 8.

    Mozilla Firefox, "RFP breaks jigsaw-puzzle-type CAPTCHAs," bugzilla.mozilla.org, Bug 1747674. https://bugzilla.mozilla.org/show_bug.cgi?id=1747674

FAQ

A script uses CanvasRenderingContext2D.measureText() to render a test string in a probe font and measures the pixel width. If the width differs from the fallback font baseline, the probe font is installed. If identical, it is absent. No canvas image export is required; the detection uses a number comparison.

No. Font detection through measureText() does not export any canvas pixel data. It only reads the measured width of a text string, which is a number. Blocking HTMLCanvasElement.toDataURL()` or toBlob() prevents canvas image fingerprinting but has no effect on font probing through measureText()`.

Application-specific fonts are most distinctive: Adobe Fonts (Creative Cloud), JetBrains Mono or Fira Code (developer IDEs), vendor fonts from gaming peripherals, and specialty fonts from professional tools. CapyToolkit can show which installed fonts remain visible after you enable browser protection. OS default fonts are widely shared and less distinguishing alone, but confirming Segoe UI still identifies Windows precisely.

Yes. Brave with Standard Shields active limits the font list returned through canvas measurement to a common subset. Application-specific fonts appear as absent even if they are actually installed. This matches the font probe results of many other Brave users, providing herd anonymity rather than exposing the real installation.

Scripts can probe hundreds of fonts in under 100 milliseconds. Practical fingerprinting implementations typically probe 30 to 200 fonts, focusing on fonts that vary predictably across OS versions and software portfolios. The W3C fingerprinting guidance notes that 30 to 50 targeted probes are sufficient to produce a highly distinctive fingerprint.

WebGL and AudioContext Fingerprinting

For high-entropy browser tracking, WebGL and AudioContext are among the most revealing signals. WebGL exposes graphics hardware details, while AudioContext exposes CPU and audio-processing behavior through a silent test tone. These signals are harder to block than canvas because they require more invasive browser restrictions.

Reading the WebGL row

  • Null value confirms the WEBGL_debug_renderer_info extension is blocked (Brave Shields)
  • Generic string confirms spoofing (Firefox resistFingerprinting)
  • Empty row confirms WebGL is disabled (Tor Browser, Safer/Safest levels)

Opens the Browser Fingerprint Inspector with this section's checklist shown at the top of the tool.

Open in the tool →

How WebGL and AudioContext signals complement each other

WebGL and AudioContext fingerprinting read two completely different parts of your machine, which is what makes them so powerful when combined. WebGL exposes the entire graphics stack: GPU vendor, renderer name, supported extensions, texture limits, and shader precision, all returned through a single extension query.1 AudioContext exposes CPU and audio processing behavior through an offline signal that never reaches your speakers, capturing floating-point rounding differences that vary across processor architectures and driver implementations.2

Consequently, a tracker that collects both values can compare your graphics profile with your audio profile, which gives it two independent ways to recognize the same browser. Building on this, blocking only one surface can leave the other one intact, so privacy checks should review both rows in the fingerprint inspector. A browser that randomizes canvas but leaves WebGL untouched still exposes a detailed graphics profile, and one that masks the renderer string but allows unmodified audio processing still leaks CPU-level identifying information through the audio hash.

How scripts use both signals together

Scripts usually do not rely on WebGL or AudioContext in isolation because each signal alone provides only a partial view of the device. They collect a cluster of values from both APIs, hash the combined cluster, and store the resulting identifier for later comparison across sessions and sites. WebGL contributes the graphics profile with GPU-specific renderer strings, while AudioContext contributes a separate processing profile derived from CPU floating-point behavior that is unlikely to match another user's browser exactly.2

This pairing is powerful because the two signals come from different layers of the device. A browser update may change one hash, but the other can still provide continuity. When both remain stable, the tracker gains confidence that the same browser returned even if cookies, IP address, or timezone changed. This dual-signal resilience is what makes WebGL and AudioContext pairing so much harder to defeat than canvas alone: spoofing one hash without the other creates an inconsistency that advanced fingerprinting systems can detect and flag.

When browser protections change these values

When a browser protects against fingerprinting, it usually changes WebGL and AudioContext before it touches lower-entropy signals like language or timezone. Brave can return empty WebGL renderer strings and inject per-site noise into AudioContext frequency data.3 Firefox with privacy.resistFingerprinting can mask WebGL parameters and alter audio rendering output.4 Tor Browser goes further by disabling WebGL at higher security levels, which removes the graphics surface but may break maps, 3D tools, and browser games.5

How each browser's approach affects the signal

Brave's per-site farbling means the WebGL renderer string returns empty when Shields are active, while AudioContext frequency data receives noise that changes per origin. Firefox RFP masks the renderer string to a generic value and modifies audio output at the engine level. Tor Browser removes WebGL entirely at higher security levels. Consequently, you should treat WebGL and AudioContext as advanced signals that reveal how aggressive your browser protection is.

You can confirm what your browser exposes by opening the fingerprint inspector and checking the WebGL and AudioContext rows directly. A browser with strong protections shows an empty or randomized renderer string and noise in the audio curve, while an unprotected one reveals the full graphics and audio stack. Testing both before and after enabling a protection makes the difference easy to see and act on.

Running the inspector in CapyToolkit turns these differences into a visible report you can act on. The WebGL and AudioContext rows show exactly what each browser exposes, so you can compare a protected profile against an unprotected one and decide which trade-offs you are willing to accept. A quick before and after test is the most reliable way to confirm a protection is actually doing its job.

Reading both signals in CapyToolkit

Reading the Browser Fingerprint Inspector gives you a practical way to compare protection levels. Open the page in a default browser, record the WebGL renderer and AudioContext hash, then repeat the check in Brave, Firefox with resistFingerprinting, or Tor Browser. A stable WebGL string and a stable audio hash mean the page can collect detailed hardware signals. A missing renderer string, a normalized audio value, or a null result means the browser has already reduced the tracking surface. This side-by-side test shows which settings matter for your own device rather than relying on generic privacy claims. Pay special attention to whether the WebGL renderer string disappears entirely or merely changes to a generic value, because the difference between blocking and spoofing affects both your privacy level and your compatibility with WebGL-dependent sites like 3D maps and browser-based games.

If you need stronger protection than a VPN

If your privacy goal is to hide your IP address from websites and your ISP, a VPN is the right tool for that specific job. If your privacy goal is to reduce browser recognizability through fingerprinting, a VPN does not change WebGL or AudioContext values because those values come from browser JavaScript APIs that run locally, not from the network route your packets travel through.

Pair a VPN with a browser that farbles, masks, or disables high-entropy signals. Brave with Shields active, Firefox with privacy.resistFingerprinting, and Tor Browser each address these signals more directly than network tools. For everyday browsing, test the result after each change so your setup actually reduces the signals that fingerprinting scripts can read. The most effective everyday combination is a privacy-focused browser with built-in farbling plus a VPN for network-level anonymity, because the two tools address completely different layers of the tracking stack and complement each other without overlap.

How fingerprinting scripts collect WebGL and AudioContext in practice

In a real fingerprinting implementation, WebGL and AudioContext collection happens within the first few hundred milliseconds of page load, before visible content renders. A script creates an offscreen WebGL canvas using document.createElement('canvas').getContext('webgl'), then queries the WEBGL_debug_renderer_info extension immediately. The full renderer string returns synchronously without any user interaction or permission request. Simultaneously, a separate script instantiates an OfflineAudioContext, attaches an OscillatorNode and DynamicsCompressorNode, and calls startRendering() to produce the audio hash asynchronously.

Both signals combine with a canvas fingerprint collected through the 2D context on the same offscreen canvas element. Scripts bundle all three hashes into a single identifier object alongside screen dimensions, timezone, and language, then send the combined object to a first-party or third-party analytics endpoint using navigator.sendBeacon(), which fires reliably even when the user navigates away before the network request completes.6 The entire collection sequence leaves no stored data on your device and requires no browser notification or permission dialog.

Why collection timing makes blocking harder

Because WebGL and AudioContext data is collected before the page is visually complete, extension-based blockers that inject their protection code after page scripts run can arrive too late to intercept the initial collection. Browser-level protections that apply before any JavaScript executes, such as Brave's built-in farbling and Firefox RFP's engine-level interception, address this timing gap. Extensions that rely on content script injection may miss the first collection event on fast-loading pages.

GPU vendor identification and what it reveals about your device

GPU vendor identification is one of the most precise hardware classification signals in the fingerprinting stack. The WEBGL_debug_renderer_info UNMASKED_RENDERER_WEBGL string includes the GPU manufacturer, the specific model line, and the rendering backend.7 On Windows Chrome, "ANGLE (NVIDIA GeForce RTX 4070 Direct3D11 vs_5_0 ps_5_0)" identifies the manufacturer (NVIDIA), the product tier (RTX 4070), the Windows API version (Direct3D11), and the shader model (vs_5_0). On macOS Chrome, the same GPU produces "ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Pro, Unspecified Version)" instead, because macOS routes WebGL through the Metal API.

Each GPU generation within a vendor family produces a distinct model string. Adreno 740 and Adreno 750 are different strings despite being both Qualcomm mobile GPUs. This level of granularity lets fingerprinting systems determine the device's purchase year, price segment, and performance tier from a single API call. Combining the renderer string with navigator.hardwareConcurrency and navigator.deviceMemory produces a three-signal device tier fingerprint that is stable across browser updates until the user upgrades their hardware.

How platform-level blocking changes the renderer string

Brave returns null for the WEBGL_debug_renderer_info extension object when Shields are active, which prevents the renderer and vendor strings from being read entirely. Firefox with RFP spoofs both values to a generic string that does not identify the underlying GPU. Tor Browser disables WebGL entirely at the Safer and Safest security levels. You can verify which behavior your browser applies by checking the WebGL row in the fingerprint inspector: a null value confirms extension blocking, a generic string confirms spoofing, and an empty row confirms WebGL is disabled.

When to use this

Use this guide to understand advanced fingerprinting vectors and protect against GPU-based and audio-based tracking.

Examples

WebGL fingerprint signals

Before
A script queries your GPU vendor, renderer name, and supported extensions.
After
Your browser reports: Vendor: "Google Inc. (NVIDIA)", Renderer: "ANGLE (NVIDIA GeForce GTX 1080 Direct3D11...)", GL_VERSION: "OpenGL ES 3.0". This combination identifies your GPU model and driver.

`AudioContext` fingerprint before and after mitigation

Before
A script generates an audio signal and analyses the frequency response curve. Your machine produces: "94.30.15,99.17,49.15,63.81..."
After
With audio fingerprinting blocked (Firefox resistFingerprinting), the script receives null or a flat signal, preventing unique identification.
Sources
  1. 1.

    Mozilla Developer Network, "WebGL: 2D and 3D graphics for the web," developer.mozilla.org, July 2025. https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API

  2. 2.

    FingerprintJS, "audio.ts," github.com, accessed June 2026. https://github.com/fingerprintjs/fingerprintjs/blob/master/src/sources/audio.ts

  3. 3.

    Brave, "Fingerprinting defenses 2.0," brave.com, accessed June 2026. https://brave.com/privacy-updates/4-fingerprinting-defenses-2.0/

  4. 4.

    Mozilla Bugzilla, "Apply Resist Fingerprinting Protection to WebGL," bugzilla.mozilla.org, accessed June 2026. https://bugzilla.mozilla.org/show_bug.cgi?id=1428033

  5. 5.

    Tor Project, "Security levels - Features," torproject.org, accessed June 2026. https://support.torproject.org/tor-browser/features/security-levels/

  6. 6.

    Mozilla Developer Network, "Navigator: sendBeacon() method," developer.mozilla.org, accessed June 2026. https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon

  7. 7.

    Crawlex, "AudioContext fingerprinting: the OscillatorNode signature explained," crawlex.net, accessed June 2026. https://blog.crawlex.net/blog/audiocontext-fingerprinting/

FAQ

WebGL fingerprinting collects your GPU vendor, renderer name, maximum texture size, supported extensions, and rendering precision. These values are unique enough to identify specific GPU and driver combinations.

A script generates an oscillator tone, passes it through an analyser, and reads the frequency response curve. Minor hardware and driver differences produce unique frequency response patterns, usable as a fingerprint.

Yes. WebGL Report (webglreport.com) shows your full WebGL capabilities. CapyToolkit's Browser Fingerprint Inspector displays the signals your browser exposes to fingerprinting scripts.

Tor Browser fully blocks WebGL by default. Firefox with resistFingerprinting masks some WebGL values. Brave randomizes certain WebGL outputs. No browser fully blocks AudioContext fingerprinting yet.

Disabling WebGL breaks WebGL-dependent applications like 3D viewers, maps, and some video players. Blocking extensions offer a middle ground; they can randomize WebGL values while keeping WebGL functional.

Check Which Client Hints Your Browser Sends

Your browser describes itself to every server it contacts, and part of that description now travels as Client Hints. In Chromium browsers, User-Agent Client Hints replace the User-Agent string with a structured API: the Sec-CH-UA request header and the navigator.userAgentData JavaScript API provide a structured, graduated alternative to the legacy User-Agent string. By default, every Chromium request includes three low-entropy hints: Sec-CH-UA with the browser brand and major version, Sec-CH-UA-Mobile indicating mobile or desktop, and Sec-CH-UA-Platform with the OS name.1 These default hints expose less information than the legacy UA string, which included the full OS version and platform architecture.

Yet servers can request high-entropy values: platform version, CPU architecture, device model, and full browser version can be requested by including an Accept-CH response header, and scripts can also call navigator.userAgentData.getHighEntropyValues() directly to obtain the same data without a server round-trip.2 Consequently, the privacy benefit of UA-CH depends entirely on which hints a server requests and whether the browser honors those requests without user awareness.

High-entropy hints requiring Accept-CH

  • Sec-CH-UA-Arch CPU architecture
  • Sec-CH-UA-Platform-Version OS version
  • Sec-CH-UA-Model device model
  • Sec-CH-UA-Full-Version-List full browser version, minor and patch included

Opens the Browser Fingerprint Inspector with this section's checklist shown at the top of the tool.

Open in the tool →

What low-entropy UA-CH hints expose automatically

The three default Client Hints, Sec-CH-UA, Sec-CH-UA-Mobile, and Sec-CH-UA-Platform, are sent on every same-origin and cross-origin request from Chromium-based browsers without requiring any server permission. Sec-CH-UA includes a JSON-encoded list of browser brands and their major versions: a typical value is '"Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99".3 This list identifies the browser family and major version, which is slightly less entropy than the legacy UA that included the build version.

Sec-CH-UA-Mobile is a boolean that returns ?1 for mobile browsers and ?0 for desktop, which narrows the device class. Sec-CH-UA-Platform returns the OS name as a string: "Windows", "macOS", "Android", "Linux". Consequently, a server can determine OS and browser family from these three default headers without any explicit permission request. Building on this, the "Not-A.Brand" entry in Sec-CH-UA is a deliberate obfuscation to prevent brand-list-based browser detection from becoming too precise.3

High-entropy hints requiring explicit server permission

Servers that want more detailed browser information can request high-entropy Client Hints via the Accept-CH response header. Responding with "Accept-CH: Sec-CH-UA-Arch, Sec-CH-UA-Platform-Version, Sec-CH-UA-Model" instructs the browser to send CPU architecture, OS platform version, and device model on subsequent requests to that origin. JavaScript scripts can call navigator.userAgentData.getHighEntropyValues(['architecture', 'platformVersion', 'model', 'uaFullVersion']) to request the same information without a server round-trip.

How high-entropy hints restore what UA Reduction removed

The returned object contains values like platformVersion: "15.0", architecture: "x86", bitness: "64", and the full browser version including minor and patch numbers, which means a single Accept-CH header restores most of the entropy that UA Reduction removed from the legacy User-Agent string.4 Furthermore, the getHighEntropyValues() call returns a Promise rather than synchronous data, which means fingerprinting scripts must await the result, introducing a brief asynchronous step that separates Client Hints collection from synchronous canvas and WebGL fingerprinting. This permission model is what distinguishes Client Hints from the legacy UA string: the server must explicitly ask for high-entropy data rather than receiving it unconditionally on every request.

The inspector does not read Client Hints; its User Agent and Platform rows show the legacy values those hints were designed to replace, while the Network panel in your browser's developer tools shows which hints a site requested and which ones your browser sent. Comparing that output against Firefox or Safari, which send no Client Hints at all, shows how much of the surface is Chromium-specific.

Privacy implications for developers sending Accept-CH headers

From a developer perspective, Accept-CH is a straightforward way to recover the detailed browser version information that UA Reduction removed from the legacy User-Agent string. From a privacy perspective, requesting high-entropy hints through Accept-CH headers increases the browser fingerprint entropy of every user who visits the site beyond what the browser would normally expose. Each Accept-CH value requested adds an additional dimension to the fingerprint surface the server receives, and requesting all available high-entropy hints effectively restores the full entropy that UA Reduction was designed to eliminate.

How to use client hints responsibly

Consequently, developers who include Accept-CH headers for analytics or compatibility purposes are expanding their users' fingerprint exposure beyond what the browser's defaults provide. The W3C recommends that servers request only the hints they need for legitimate functionality rather than requesting all available values. Building on this, Firefox does not implement Client Hints at all; it continues to send the legacy User-Agent string and never sends Sec-CH-UA headers. Safari also does not send Client Hints.5 Therefore, Client Hints are a Chromium-specific fingerprinting surface, and servers that rely on them for compatibility detection receive no equivalent data from Firefox and Safari users. A narrow request list is usually the safest compromise between compatibility and privacy.

Auditing Accept-CH headers with browser DevTools

Auditing which high-entropy Client Hints a site requests takes less than a minute in Chrome DevTools. Open DevTools, navigate to the Network panel, reload the page, click the top-level document request, and examine the Response Headers tab. An Accept-CH header listing Sec-CH-UA-Arch and Sec-CH-UA-Platform-Version means the server will receive your CPU architecture and exact OS version on all subsequent requests to that origin. The Permissions-Policy header on the same response may also grant cross-origin subframes permission to request hints, extending the data collection to third-party analytics or advertising scripts embedded in the page.

After identifying which hints a site requests, check the Request Headers of a subsequent navigation to confirm that your browser is honoring the Accept-CH directive. Chrome sends the requested hints automatically without any user notification. A Sec-CH-UA-Arch header in your outgoing requests confirms that the server received your CPU architecture, restoring a dimension of detail that UA Reduction was designed to remove from the passive User-Agent string.

Using the Permissions Policy header to restrict hint sharing

If you control a web property that embeds third-party scripts, you can use the Permissions-Policy response header to prevent those third-party iframes from requesting high-entropy hints without your consent. Adding "Permissions-Policy: ch-ua-arch=(), ch-ua-platform-version=()" to your response headers blocks the embedded scripts from receiving those values, even if they send their own Accept-CH headers. This is the recommended pattern for operators who want to send hints to their own analytics endpoint without exposing the same data to every embedded third-party script on the page.

How Client Hints compare to legacy User-Agent string fingerprinting

The User-Agent string and Client Hints represent two different philosophies for browser identification. The legacy UA string sent all available information unconditionally on every request, exposing the full OS version, CPU architecture, browser build version, and rendering engine in a single passive header. Client Hints replace this with a graduated disclosure model where low-entropy values are sent by default and high-entropy values require explicit server permission.

From a fingerprinting perspective, the two approaches produce similar results when servers use Accept-CH to request all available high-entropy hints. The difference is visibility: Accept-CH requests are logged in Network DevTools, making them auditable, whereas the legacy UA string exposed equivalent data silently. This auditability is the privacy improvement that Client Hints actually deliver, not a reduction in available fingerprinting data.

Firefox and Safari users are unaffected by UA Reduction because neither browser adopted the Client Hints API. Both continue to send a legacy User-Agent string, and Firefox's string still includes the full platform version. Checking the request headers in DevTools, next to the User Agent row in the fingerprint inspector, shows whether a server's Accept-CH headers are capturing high-entropy data from Chrome users while falling back to legacy UA strings from Firefox and Safari visitors.

When to use this

Use this guide when auditing which Client Hints a server requests from your browser, or when implementing Accept-CH in a web application and weighing the privacy tradeoffs of which high-entropy hints to request from users.

Examples

Checking which Client Hints a server requests

Before
Open DevTools > Network panel, reload the page, select the initial document request, and check the Response Headers tab for an Accept-CH header.
After
A site sending "Accept-CH: Sec-CH-UA-Arch, Sec-CH-UA-Platform-Version" is requesting your CPU architecture and OS version. Your browser will send these on subsequent requests to this origin.

Not all sites request high-entropy hints. The presence of `Accept-CH` in response headers is the signal to check.

Reading high-entropy values from `navigator.userAgentData`

Before
navigator.userAgentData.brands returns the low-entropy brand list immediately and synchronously.
After
navigator.userAgentData.getHighEntropyValues(["architecture", "platformVersion"]) returns a Promise. Awaiting it yields { architecture: "x86", platformVersion: "15.0" }; values not available in the synchronous UA string after UA Reduction.
Sources
  1. 1.

    IETF, "HTTP Client Hints," RFC 8942, IETF, February 2021. https://httpwg.org/specs/rfc8942.html

  2. 2.

    Mozilla Developer Network, "NavigatorUAData.getHighEntropyValues()," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/API/NavigatorUAData/getHighEntropyValues

  3. 3.

    Chrome Developer, "User-Agent Client Hints," developer.chrome.com, accessed July 2026. https://developer.chrome.com/docs/privacy-security/user-agent-client-hints

  4. 4.

    Mike Taylor and Yoav Weiss, "User-Agent Client Hints," W3C Web Platform Incubator Community Group, February 2026. https://wicg.github.io/ua-client-hints/

  5. 5.

    Mozilla Developer Network, "User-Agent Client Hints API," developer.mozilla.org, accessed July 2026. https://developer.mozilla.org/en-US/docs/Web/API/User-Agent_Client_Hints_API

FAQ

Sec-CH-UA is a request header sent automatically by Chromium browsers containing the browser brand list and major version. It is always accompanied by Sec-CH-UA-Mobile (mobile/desktop boolean) and Sec-CH-UA-Platform (OS name string). CapyToolkit's fingerprint inspector doesn't read Client Hints, so use your browser's developer tools to see whether it sends these low-entropy hints on the current page. These three low-entropy hints are sent on every request without any server permission required.

A server can request Sec-CH-UA-Arch (CPU architecture), Sec-CH-UA-Platform-Version (OS version), Sec-CH-UA-Model (device model), Sec-CH-UA-Bitness (32/64-bit), and Sec-CH-UA-Full-Version-List (full browser version including minor and patch) via the Accept-CH response header.

No. Firefox does not implement the Client Hints API and does not send Sec-CH-UA headers. Firefox continues to send the legacy User-Agent string. Accept-CH headers from servers are ignored by Firefox. Safari also does not implement Client Hints.

With high-entropy hints (architecture, platform version, full version), the data is equivalent to the pre-UA-Reduction User-Agent string. Combined with canvas, WebGL, and audio signals, high-entropy Client Hints increase the fingerprint entropy meaningfully. Default low-entropy hints (brand, mobile, platform) alone provide modest entropy.

A site requesting Sec-CH-UA-Arch and Sec-CH-UA-Platform-Version is collecting your CPU architecture and OS version, information that narrows your fingerprint. Sites should request only the hints they need for legitimate compatibility or functionality purposes. Requesting all available high-entropy hints without a clear purpose is a privacy concern.

FAQ

No single one does. Canvas, fonts and WebGL each separate devices well, and scripts combine them because the combination is far more distinctive than any one value. That is also why the inspector reports an overall score next to the individual rows.

Only by giving up some features. Tor Browser disables or standardises most of them, at the cost of sites that rely on WebGL or canvas. Brave and Firefox take a middle path: they add noise to some values or report generic ones while keeping pages working.

They protect in different ways. Blocking returns an empty value, which is itself unusual and can stand out. Noise returns a plausible value that changes between sites or sessions, so a script cannot link your visits with it. Brave's per-site noise keeps the value stable on one site and different on the next.

No. Websites can read all of these signals without asking for permission, which is what makes fingerprinting hard to notice. The inspector reads them the same way a tracking script would, so what you see is what a site sees.

No. None of these techniques stores anything on your device. They are measured again on each visit from your hardware, software and settings, so clearing cookies or site data leaves them unchanged.

No. CapyToolkit doesn't store or upload the results; the canvas, font, WebGL and audio checks all run in your browser.

Additional resources

Guides

How to Reduce Your Browser Fingerprint Practical steps to reduce your browser fingerprint including Firefox resistFingerprinting, Tor Browser, Brave, and canvas-blocking extensions. Lower your uniqueness score. How Browser Fingerprinting Techniques Work How canvas, font, WebGL, AudioContext and Client Hints fingerprinting work, what each one reveals about your device and how browsers block or change each signal. Is Browser Fingerprinting Legal Under GDPR Browser fingerprinting may constitute personal data processing under GDPR and ePrivacy Directive. Consent requirements, legitimate interest, and regulatory enforcement approaches. Safari Fingerprinting on iPhone and Mac What Safari exposes to fingerprinting on iPhone and Mac, what Advanced Fingerprinting Protection in iOS 26 changes, and which signals it leaves untouched. Playwright and Puppeteer Browser Fingerprinting Playwright and Puppeteer expose navigator.webdriver=true, empty plugins, and SwiftShader WebGL in headless mode. How anti-bot systems detect automation and how to test more realistically. Android Chrome Browser Fingerprint Android Chrome exposes navigator.deviceMemory, hardwareConcurrency, and full WebGL renderer strings that iOS Safari restricts. How Android fingerprints differ from iOS, and Brave for Android protection. Windows 11 Browser Fingerprint Windows 11 creates distinctive fingerprints through its exclusive font set, ANGLE WebGL rendering strings, and non-integer DPI scaling options. Which signals are most identifying and how to reduce them.