Skip to content
Skip the acts, go to the labLeave

This is what I know about you without you ever telling me.

0.0 bits so far

  1. User agent
  2. Screen
  3. Locale
  4. Preferences
  5. Canvas
  6. GPU strings
  7. GPU render
  8. Audio stack
  9. Fonts
  10. CPU and memory
  11. Pointer

No button, no prompt, no permission. Reading this page is the whole interaction, and it started when the page opened.

the meter, the ticker, and motion off

The meter in the corner is the running total of what has been read, in bits, and it climbs on its own. The line along the bottom is on a timer rather than on your scrolling.

With motion switched off at the system level, or paused in the site nav, the acts arrive without animating, the entrance does not hold the page, and both of those keep working as text. Nothing here is hidden until something moves.

Start with the things you would have guessed.

Every site you have loaded could read all of this, and most of them did. Boring on purpose, so the next act is easier to believe.

Every rarity figure here is counted from this corpus, not from published averages, and arrives with the corpus size attached.

Now the part nobody agreed to.

None of these needed a permission prompt. Drawing text or rendering canvas shapes is the same work a page does to lay out a paragraph.

Panel notes come from these collectors run on Chrome 150, Edge 150, Firefox 153, Firefox with resistFingerprinting and WebKit 26.5, July 2026.

Three signals this page could have claimed and does not.

the three it does not collect
  • WebRTC no longer hands over a local IP, only an mDNS name that changes.
  • The battery API is present only on Chromium, and absent on Firefox and WebKit.
  • The Local Font Access API works on Chromium and asks permission first. A signal that asks first is not what this page is about.

A page that claimed the first two and quietly failed would be worse than one that leaves them out.

What that adds up to.

No new measurement. The same eleven readings, plus a timer and a scroll position, read as conclusions.

  1. That address resolves to Columbus, OH. Nobody asked you and nothing prompted: an address is what a connection is made of, so every server you reach gets this much whether or not it does anything with it. This page is telling you because most of them do not.

    request / a plausible visitor could produce this innocently / where it stops

    A city here is where your provider registered the address, which is their equipment rather than you. Measured on 31 July 2026 against a fixed home line: this named a town roughly an hour's drive from the machine making the request, and the same person on a German connection is routinely placed in a neighbouring village. The difference is the provider's block assignment, not the lookup getting cleverer, so the error is worth anywhere from a street to a region depending on who sells you the connection. On a mobile network one gateway can carry a whole region, and behind a VPN it is the exit node. Read from this request and never written, the same as the address above.

  2. Your request arrived from 216.73.216.109. The CDN in front of this page tagged it US.

    request / follows from the value / where it stops

    Read from the request headers on the server and returned in this one response. It is not written to any table, and there is no column for it in the schema. Act 7 shows that schema.

Hand-written templates with explicit conditions, not a model producing text, so every line can be checked against what the collectors observed: src/features/glasshouse/inference.ts.

How rare that makes you.

Asking the corpus.

Reload the page.

Asking.

Hard reload. Clear your cookies. Come back tomorrow. Nothing is written to your browser to make this work, and nothing that could name you is written to ours.

The value, and how to compare it

Your browser derives this from ten of the eleven signals. Computed here, shown here, thrown away when you leave. Never sent anywhere, because a joint value over your components is the one thing this design refuses to store. Photograph it, reload, compare.

Durable, ten signals
measuring
The same plus canvas
measuring

A prediction, so this is falsifiable. On Chrome and Edge both lines survive a reload and a full restart. On Firefox 151 and later the first survives both and the second changes once you quit and reopen the browser, because Firefox rerolls canvas noise per browsing session. That is why canvas is counted but never allowed to vote on whether you have been here.

Most privacy tweaks make this worse.

Waiting for the corpus, because there is nothing to recompute until your own counts come back.

Spoof one reading and the rest still describe the machine you have, and the contradiction is easier to spot than the thing it hid. Randomise every call and you are uniquely unstable, which is durable on its own.

Blending in is the only thing in the list that helps, and it is the one that feels like doing nothing.

Where these numbers come from

Every switch recomputes your own counts against the real corpus. Nothing is simulated and no figure in this act is typed into the copy, so where the arithmetic disagrees with the two lines above, the arithmetic is printed anyway.

The switches act on this. A component with one dominant value is somewhere to hide; one where nearly every visit is its own value is not.

Reading the corpus.

None of this needs a stored fingerprint. Uniqueness is a sum of per-component terms, so a switch either drops a term or substitutes a count, which is the property Act 7 is about.

Everything this page just wrote down.

Waiting on the write.

Zero PII storage contract: no visitor table, no IP address, no joint fingerprint, no account links, and zero raw values stored.

Each row increments one component count independently. Rarity is calculated at read time without ever persisting a joint profile.

Values are stored as a 40 bit salted HMAC, truncated on purpose so that collisions are guaranteed.

Why the hash is that narrow

Different values share a hash by design. The salt lives only on the server, so a database dump is just opaque truncated counts.

Three things this design gives away

The write times are a join key

Every row carries updated_at. Grouping rows by write time reconstructs which values arrived together.

It reveals a timestamp to your own browser when you submit a matching value, and nothing to anyone else.

This page sets a cookie

The visit route sets glasshouse_visit_session to scope the rate limiter. Recognition does not use it.

The rate limiter hashes your IP, and for a while it did it badly

The rate-limit table stores a keyed HMAC of your address, deleted after ten minutes.

It previously used keyless SHA-256 before being upgraded to a server-salted HMAC. The corpus tables hold no addresses.

What actually helps

Use a browser that standardises rather than randomises. Spoofing two signals leaves nine describing your exact machine plus a contradiction.

Prior art. EFF’s Cover Your Tracks and amiunique.org measure uniqueness against much larger corpora. webkay lists what a browser exposes. clickclickclick.click does the live narration better. The signals here are public knowledge and none was discovered here.

Two machines built to the same specification produce the same readings. This page cannot tell them apart and neither could a stored fingerprint, which is a limit of the technique rather than of this implementation.