Skip to content

Fingerprint Spoofing ​

Nothing Browser injects a spoofing script at DocumentCreation phase β€” before any page JavaScript runs. This makes fingerprinting almost impossible.


Overview ​

Browser fingerprinting is how websites identify you without cookies. Nothing Browser spoofs the most common fingerprinting vectors.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    FINGERPRINT SPOOFING                                      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                             β”‚
β”‚  Real Machine:                    Spoofed to Website:                       β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”‚
β”‚  β”‚ CPU: 16 cores       β”‚   ───►   β”‚ CPU: 8 cores        β”‚                  β”‚
β”‚  β”‚ RAM: 32 GB          β”‚   ───►   β”‚ RAM: 8 GB           β”‚                  β”‚
β”‚  β”‚ GPU: NVIDIA RTX     β”‚   ───►   β”‚ GPU: Intel Iris     β”‚                  β”‚
β”‚  β”‚ OS: Linux           β”‚   ───►   β”‚ OS: Windows         β”‚                  β”‚
β”‚  β”‚ Canvas: Unique      β”‚   ───►   β”‚ Canvas: Noised      β”‚                  β”‚
β”‚  β”‚ Audio: Unique       β”‚   ───►   β”‚ Audio: Noised       β”‚                  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

How It Works ​

On first launch, IdentityGenerator reads real machine values and generates a BrowserIdentity:

cpp
// IdentityGenerator reads real values:
id.cpuCores      = std::thread::hardware_concurrency();   // real CPU
id.ramGb         = readFromProcMeminfo();                  // real RAM, rounded to power of 2
id.screenW/H     = QGuiApplication::primaryScreen()->size();
id.timezone      = QTimeZone::systemTimeZoneId();
id.canvasSeed    = rng->generateDouble();  // random per session
id.audioSeed     = rng->generateDouble();  // random per session

Key Principles ​

PrincipleDescription
Real values as baseCPU cores, RAM, screen size come from your machine
Noise added on topCanvas and audio get per-session noise
Consistent per sessionSame fingerprint throughout the session
Different across sessionsNew noise seeds = new fingerprint

Real values are used as the base. Only the canvas/audio/webgl seeds change each session β€” so APIs are internally consistent (CreepJS cross-validation passes) but differ across sessions (tracking fails).


What Is Spoofed ​

PropertyReal ValueSpoofed Value
navigator.webdriverundefined (real browser)undefined
navigator.vendorGoogle Inc.Google Inc.
navigator.maxTouchPoints0 (desktop)0
navigator.pdfViewerEnabledtruetrue
navigator.pluginsChrome pluginsChrome PDF Plugin, Viewer, Native Client
navigator.mimeTypesapplication/pdf, text/pdfSame

Screen Properties ​

PropertyReal ValueSpoofed Value
screen.widthReal screen widthReal screen width
screen.heightReal screen heightReal screen height
screen.availWidthReal widthReal width
screen.availHeightReal height - 40 (taskbar)Real height - 40
screen.colorDepth2424
devicePixelRatioReal DPI ratioReal DPI ratio

Canvas Noise ​

Canvas reads are intercepted. Each pixel gets Β±1 noise using a seeded PRNG:

javascript
// Seed changes every session β€” same within a session
function seededRand(seed, index) {
    const x = Math.sin(seed * 9301 + index * 49297 + 233720) * 24601;
    return x - Math.floor(x);
}

Both getImageData and toDataURL are intercepted. The same seed is used for both β€” so they are internally consistent.

Audio Noise ​

AudioBuffer.getChannelData and copyFromChannel add Β±0.00000005 noise per sample using a separate seed:

CharacteristicValue
Noise amountΒ±0.00000005
AudibilityInaudible
DetectionDifferent hash every session

WebGL Spoofing ​

javascript
// Intercepts getParameter for aliased range params
WebGLRenderingContext.prototype.getParameter  // spoofed
WebGL2RenderingContext.prototype.getParameter // spoofed
ParameterReal ValueSpoofed Value
UNMASKED_VENDOR_WEBGLNVIDIA/AMDIntel Inc.
UNMASKED_RENDERER_WEBGLActual GPUIntel Iris OpenGL Engine

Chrome Object ​

A full window.chrome object is injected:

javascript
window.chrome = {
    runtime: { ... },
    loadTimes: function() { ... },
    csi: function() { ... },
    app: { ... }
};

WebRTC Protection ​

STUN servers are filtered from RTCPeerConnection config to prevent IP leakage:

javascript
// Before: Your real IP could leak
iceServers: [{ urls: "stun:stun.l.google.com:19302" }]

// After: STUN servers removed
iceServers: []  // No IP leakage

Performance Timer ​

performance.now() is rounded to 0.1ms precision β€” reduces timing attack surface.

Battery API ​

javascript
navigator.getBattery() // β†’ { charging: true, level: 0.85-1.0, ... }

Timezone ​

Intl.DateTimeFormat is proxied to inject the real system timezone where not specified.


Injection Timing ​

The fingerprint spoofing script is injected at DocumentCreation β€” before any page JavaScript runs.

Timeline:
────────────────────────────────────────────────────────────────────────────►

DocumentCreation     Page JavaScript     User Interaction
       β”‚                    β”‚                   β”‚
       β–Ό                    β–Ό                   β–Ό
  Fingerprint        Page sees already    Everything works
  injected           spoofed values       normally

Why DocumentCreation Matters ​

Injection PointCan Page Detect?Why
DocumentCreation❌ NoScript runs before page code
After page load⚠️ MaybePage can see when values changed
Via CDP (Puppeteer)βœ… YesAutomation detectable

QWebEngineScript with DocumentCreation is architecturally superior to CDP-based injection.


Session Identity ​

Where Identity Is Stored ​

The identity is saved at:

~/.config/nothing-browser/identity.json

Identity File Example ​

json
{
  "cpuCores": 8,
  "ramGb": 16,
  "screenWidth": 1920,
  "screenHeight": 1080,
  "timezone": "America/New_York",
  "canvasSeed": 0.123456789,
  "audioSeed": 0.987654321,
  "createdAt": 1700000000000
}

Resetting Identity ​

To generate a new permanent identity:

bash
# Delete the identity file
rm ~/.config/nothing-browser/identity.json

# Restart browser β€” new identity generated

In-app reset button coming in future version.


Testing Fingerprint ​

Online Test Sites ​

SiteWhat It Tests
amiunique.orgComplete fingerprint analysis
browserleaks.comCanvas, WebGL, audio
fingerprintjs.com/demoCommercial fingerprinting
creepjs.comAdvanced detection tests
deviceinfo.meDevice information

Manual Test ​

javascript
// Run in browser console
console.log({
    webdriver: navigator.webdriver,
    plugins: navigator.plugins.length,
    languages: navigator.languages,
    webglVendor: document.createElement('canvas').getContext('webgl')?.getParameter(37445),
    hardwareConcurrency: navigator.hardwareConcurrency,
    deviceMemory: navigator.deviceMemory
});

Comparison with Other Browsers ​

FeatureNothing BrowserBraveFirefoxChrome
DocumentCreation injectionβœ… Yes⚠️ Partial❌ No❌ No
Canvas noiseβœ… xorshiftβœ… Sin-based❌ No❌ No
Audio noiseβœ… Yesβœ… Yes❌ No❌ No
WebGL spoofingβœ… Yesβœ… Yes❌ No❌ No
WebRTC protectionβœ… Yesβœ… Yes⚠️ Partial❌ No
Per-session randomizationβœ… Yesβœ… Yes❌ No❌ No

Known Limitations ​

LimitationStatusFix Version
Canvas uniqueness 99.98%πŸ”¨ In progressv0.1.4
sec-ch-ua brand formatπŸ”¨ In progressv0.1.4
navigator.userAgentData missingπŸ”¨ In progressv0.1.4
WebGL UNMASKED params spoofingπŸ”¨ In progressv0.1.4

Why Canvas Uniqueness Isn't 100% ​

The sin() PRNG is being replaced with xorshift in upcoming versions to improve this.


Enabling / Disabling ​

Fingerprint spoofing is always enabled in Nothing Browser. There is no toggle.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                                                             β”‚
β”‚   There is no "disable fingerprint spoofing" button.        β”‚
β”‚                                                             β”‚
β”‚   Privacy is on by default.                                 β”‚
β”‚   Fingerprinting is blocked by default.                     β”‚
β”‚                                                             β”‚
β”‚   If you need real fingerprints, use regular Chrome.        β”‚
β”‚                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Technical Details ​

Injection Code (Simplified) ​

javascript
(function() {
    'use strict';
    
    // Spoof navigator properties
    Object.defineProperty(navigator, 'webdriver', {
        get: () => undefined
    });
    
    // Spoof plugins
    Object.defineProperty(navigator, 'plugins', {
        get: () => [
            { name: 'Chrome PDF Plugin' },
            { name: 'Chrome PDF Viewer' },
            { name: 'Native Client' }
        ]
    });
    
    // Spoof WebGL
    const getParameter = WebGLRenderingContext.prototype.getParameter;
    WebGLRenderingContext.prototype.getParameter = function(parameter) {
        if (parameter === 37445) return 'Intel Inc.';
        if (parameter === 37446) return 'Intel Iris OpenGL Engine';
        return getParameter.call(this, parameter);
    };
    
    // Canvas noise
    const originalGetImageData = CanvasRenderingContext2D.prototype.getImageData;
    CanvasRenderingContext2D.prototype.getImageData = function(x, y, w, h) {
        const imageData = originalGetImageData.call(this, x, y, w, h);
        // Add noise using xorshift
        for (let i = 0; i < imageData.data.length; i += 4) {
            if (seededRand() < 0.5) {
                imageData.data[i] ^= 1;
                imageData.data[i+1] ^= 1;
                imageData.data[i+2] ^= 1;
            }
        }
        return imageData;
    };
})();

Next Steps ​


Nothing Ecosystem Β· Ernest Tech House Β· Kenya Β· 2026

MIT Licensed | Built by Ernest Tech House Β· Kenya Β· 2026