Skip to main content

Greetings and music there

Saturday, May 16

T

Visitor Intelligence Center

Real-time browser forensics & collective visitor behaviour — no cookies, no PII, pure aggregate signal. Every number here is a shadow of someone who wandered in.

YOUR BROWSER DOSSIER— extracted the moment you loaded this page

COLLECTIVE VISITOR PATTERNS— all guests, all time, zero PII

HOW THE WEB WATCHES YOU— the deep nerdy explainer

Every browser is a leaky vessel of signals. No login required, no consent needed — here is a field guide to every technique being used on you right now, with the technical precision that most privacy articles lack.

Storage Vectors

Session vs. Persistent Cookies

Session cookies live in memory and evaporate when the tab closes — like DRAM. Persistent cookies are written to disk with a Max-Age or Expires header set by the server. Most ad networks write 90-day+ cookies on first load. The Set-Cookie SameSite=None; Secure combination is what allows cross-site identity stitching — every time you visit a site with a Facebook Like button, Facebook's cookie is refreshed, updating your timestamp in their graph.

localStorage / IndexedDB — The Evercookie

Researcher Samy Kamkar's evercookie (2010) stored a unique ID across 13 different storage mechanisms simultaneously: cookies, localStorage, sessionStorage, IndexedDB, userData (IE), Web SQL, PNG pixel caching in canvas, ETags, window.name, HTTP cache, CSS :visited history, and AudioContext state. Clear one — the ID is resurrected from the others. Modern browsers partitioned third-party storage to counter this, but same-origin evercookies still work.

CNAME Cloaking — First-Party Trackers in Disguise

Third-party cookie restrictions only block cookies from different eTLD+1 domains. Clever ad networks started using CNAME records to create subdomains that resolve to tracker infrastructure: analytics.yoursite.com → trackernetwork.io. The browser sees a same-site request — restrictions don't apply. Safari's ITP now detects CNAME cloaking via TTL analysis. Firefox blocks known CNAME trackers via uBO. Chrome does not.

Fingerprinting Techniques

Canvas Fingerprinting — Pixel-Level Uniqueness

A hidden <canvas> element draws a string of text using specific font, size, and color, then overlays shapes and gradients. ctx.getImageData() reads back the pixel RGBA values as a Uint8ClampedArray. Differences in GPU rasterisation, subpixel antialiasing, and installed fonts produce subtly different pixel values per device. The SHA-256 of that array is your canvas fingerprint. EFF's Panopticlick found it provides ~10 bits of entropy — uniquely identifying ~1 in 1,024 browsers.

AudioContext Fingerprinting

The Web Audio API creates an OscillatorNode (triangle wave, 10kHz), routes it through a DynamicsCompressorNode and AnalyserNode, and reads float32 sample values from the buffer. CPU floating-point unit differences and audio driver quirks produce slightly different values across hardware. The sum of the first 5,000 sample values gives ~8 bits of entropy. This technique was added to most fingerprinting libraries around 2016 precisely because it's invisible, requires no permissions, and works even with WebGL blocked.

Font Enumeration via CSS Measurement

A probe div is rendered with a generic fallback font (monospace, sans-serif), its offsetWidth measured. The font-family is then swapped to each of ~700 test font names. If the width changes, the font is installed. No file system access, no permissions — pure DOM measurement. Installed fonts leak OS version, language pack, and software (e.g., Adobe fonts signal Creative Cloud). Combined across 700 probes, font lists contribute ~13 bits of entropy — the highest entropy fingerprinting signal in most browser profiles.

WebGL Deep Fingerprinting

Beyond RENDERER and VENDOR strings (shown in your dossier), the WebGL context exposes: MAX_TEXTURE_SIZE, MAX_VIEWPORT_DIMS, MAX_VERTEX_ATTRIBS, supported extensions list, and GLSL shader precision formats. Each GPU model has a unique capability profile. Running a WebGL benchmark renders a 3D scene and measures pixel output from a fragment shader — differences in driver-level floating point reveal exact GPU revision. WebGL fingerprinting contributes ~10 bits of entropy and cannot be spoofed without breaking WebGL functionality entirely.

Battery API as Covert Tracking Channel

The Battery Status API (W3C spec, 2015) exposed level (0.0–1.0 in 1% steps), charging (boolean), chargingTime, and dischargingTime. Researchers showed the combination of level + chargingTime forms a ~32-bit identifier that persists across tabs, private windows, and cookie clears. A tracker could match anonymous and logged-in sessions by battery signature. Mozilla removed it from Firefox 52 (2017). Chrome never implemented it for Android. The W3C deprecated the spec entirely. It remains in Safari on macOS with reduced precision.

CSS Media Query & Sensor Fingerprinting

window.matchMedia() probes device characteristics without any API permissions: (prefers-color-scheme: dark) reveals OS theme, (pointer: coarse) reveals touchscreen, (hover: none) reveals mobile, (update: slow) reveals e-ink display, (forced-colors: active) reveals accessibility mode. Combined with screen.orientation, devicePixelRatio, and innerWidth, these queries produce a device class fingerprint. The -webkit-device-pixel-ratio media query also exposes fractional DPR on certain Android devices, pinpointing exact phone model families.

Network & Protocol Layer

TLS / JA3 Fingerprinting

Every TLS handshake starts with a ClientHello packet that lists: TLS version, cipher suite order, extension list, elliptic curve groups, and EC point formats. The JA3 algorithm (Salesforce, 2017) hashes these fields into a 32-character MD5 string — a browser fingerprint at the network layer, invisible to the browser itself. JA4 (2023 successor) adds sorting and deduplication for robustness. Web servers and CDNs log JA3/JA4 hashes per request — even a VPN can't hide it, since the fingerprint is in the TLS handshake, not the HTTP headers.

HTTP/2 HPACK Fingerprinting

HTTP/2 sends request headers in a specific order dictated by the browser's HTTP/2 stack implementation. Chrome sends :method, :authority, :scheme, :path in a distinct order with specific pseudo-header placements. Firefox's order differs. Safari's differs further. Combined with HPACK static table references and Huffman encoding preferences, the header order provides ~8 bits of entropy, uniquely identifying browser engine and version. Cloudflare's ML-based bot detection uses this signal.

Cache Timing Side-Channel

Pre-2020, CSS :visited allowed tracking browsing history: if(getComputedStyle(link).color === 'purple') { siteVisited = true }. Patched via computed style restrictions. Modern variant: cache timing. Fetch a resource from a known site, measure response time. If under ~20ms, it's in the HTTP cache — you've visited recently. Chrome's Cache Partitioning (2020) isolated caches per top-level origin+frame origin, breaking this. But DNS cache timing and connection reuse timing still leak partial history in some browsers.

The Entropy Mathematics

How Bits of Entropy Work

Entropy measures how much a signal narrows down a population. 1 bit halves it — if 50% of browsers have a given trait, knowing you have it gives 1 bit. Formula: H = -log₂(p) where p = fraction of browsers sharing this value. A screen resolution of 1920×1080 might appear in 22% of browsers → H = -log₂(0.22) ≈ 2.2 bits. The key insight: signals are independent, so bits add. The EFF's Panopticlick database showed that 8–10 signals (screen res + timezone + UA + fonts + plugins + canvas + WebGL + languages) provide 19–23 bits total — enough to uniquely identify 1 in ~8 million browsers.

The Privacy Paradox: Uncommon = Less Private

Privacy is safety in crowds. The most trackable browser is the most unusual one. Running Linux with a rare locale, a custom screen resolution, no plugins, and Tor Browser with JavaScript enabled (to avoid broken sites) makes you highly unique — exactly what fingerprinters want. Tor Browser deliberately makes all users look identical by spoofing common Windows metrics. The goal isn't to hide — it's to blend. A boring, mainstream browser profile on a mainstream OS is genuinely harder to track than an exotic privacy-hardened setup.

What We Actually Collect — Zero PII, by Design

Our guest tracker in guest-analytics.ts increments a single shared Firestore document with atomic counters: page path bucket, browser family, device class (mobile/tablet/desktop), OS family, hour of day (UTC), day of week, screen size bucket (xs/sm/md/lg/xl). There is no user ID, no IP address, no cookie, no session identifier, no fingerprint hash, no timestamp per-visit. The data is structurally incapable of identifying individuals — it is pure population-level signal, equivalent to a turnstile counter.

How to Actually Disappear

Tier 1 (most users): Firefox + uBlock Origin (hard mode) + Total Cookie Protection. Blocks 95% of trackers. Tier 2 (privacy-aware): Brave Browser — built-in shield blocks canvas/audio/font fingerprinting by injecting noise. Tier 3 (high-risk individuals): Tor Browser — identical fingerprint for all users, Tor exit nodes mask IP. VPNs only mask IP (Layer 3) — fingerprinting operates at Layer 7 (application). A VPN + Chrome is much weaker than Firefox + uBlock. The single highest-impact move: block third-party JavaScript from unknown origins.

Network API & Adaptive Loading

navigator.connection exposes your effective connection type (2G/3G/4G/wifi), estimated downlink Mbps, and RTT latency estimate in milliseconds. Responsible apps use this to serve lighter assets on slow connections — lower-bitrate audio on 2G, skip video preloading on metered connections. The saveData flag signals the user has enabled data saver mode. Your connection type is shown in your dossier above. This API is in Chrome/Android only — Firefox and Safari intentionally withheld it as a fingerprinting vector.

IntersectionObserver Ping Tracking

Ad measurement requires knowing when an ad enters the user's viewport. The IntersectionObserver API fires a callback when a target element becomes X% visible. A 1×1 transparent pixel with an IntersectionObserver sends a beacon to the ad server the moment it enters view — confirming 'viewable impression' under MRC standards (50% of pixels visible for ≥1 second). This is how display ad CPM billing verification works. No JavaScript click handler required — just the element existing in DOM and scrolling into view is enough to generate a billable event.

    Breakin Bread — The Music Platform for Independent Artists