Roadmap · research of 2026-10-26
What we plan to build next, why, and what WhiskerJS will not do. Every item rests on public sources listed at the bottom of the page.
WhiskerJS today reads 46 browser and device signals and returns them as one JSON result, without sending or storing anything. The next steps are mostly about reliability: telling apart real values from values a privacy browser has deliberately changed, keeping ids stable when browsers update, spotting automation through values that do not fit together, and testing in real browsers. New signals are added outside the id first and join it only after measurement shows they are stable. The rules stay the same: no network, no storage, no permission prompts, no console output, no verdicts.
Priority: P1 means we do it in that phase, P2 when the P1 items are done, P3 only with a clear use. Effort: S is up to two days, M up to two weeks, L more. Plans can change as measurements come in.
| Item | Priority | Effort | Why, in plain words |
|---|---|---|---|
| Spot “seeded” noise. Paint a known colour and read it back; do the same for WebGL and audio. If the value comes back changed, the browser is adding noise | P1 | S | Brave and Firefox’s fingerprinting protection add the same noise to every read in one session, so drawing twice and comparing does not catch it. A known answer does |
| Recognise more privacy modes. New browser classes for Firefox fingerprinting protection, Safari’s advanced fingerprinting protection, Tor and Mullvad, and Android WebView; Brave’s newer WebGL and WebGPU protections | P1 | M | Signals a browser randomises on purpose should be left out, not counted. Firefox 151 started bringing its protection to normal windows |
| Read capped values more than once. Check the CPU core count several times; note Chrome’s new memory buckets | P1 | S | Safari 26 gives some scripts a core count that changes between reads; Chrome 146 plans new memory buckets |
| Time and version every signal. Each signal reports how long it took and which revision of its collector ran | P1 | S | Servers can then tell which signals changed and why, and how much each one costs |
| Keep ids across upgrades. For one release after a change, also report the id computed the old way | P1 | M | A site can link the old and the new id with certainty instead of losing returning devices |
| First consistency checks. Screen size in CSS against JavaScript, time zone in Intl against Date, User-Agent against Client Hints, Playwright’s exposed functions, virtual machine and emulator graphics | P1 | M | Modern automation hides its own traces, but a faked value often disagrees with a second way of reading it |
| Cheap new signals, outside the id. Pointer and hover media queries, colour scheme, WebGL2 limits, all WebGPU limits and shader language features, full text and emoji measurements | P1 | M | Each is quick to read and needs no prompt or network; they join the id only once measured as stable |
| Real browsers in CI. Chromium, Firefox (also with its privacy settings) and WebKit; the same browser twice must give the same id | P1 | M | A simulated browser cannot show real noise or real timing |
| Size and speed budgets. Measure and publish the bundle size; let the page breathe between collectors | P1 | S | Sites choose libraries by cost; long tasks make pages feel slow |
| Ready for a public repository. Security policy, automated security checks, licence headers | P1 | S | Needed before the first release and outside contributions |
| Decide on diagnostics. An open proposal would send small, sampled diagnostic reports by default, which would change the no-network promise. It waits on the maintainer’s decision | P1 | S | The privacy promise on this site depends on it |
| Item | Priority | Effort | Why, in plain words |
|---|---|---|---|
| Engine version vector. Which JavaScript, WebAssembly and Intl features exist, and which device APIs are present (presence only) | P1 | M | Adds little to the id but shows when a browser claims to be a version it cannot be |
| Version mismatch flag. Fires when a browser claims to be older than a feature it has | P1 | S | Anti-detect browsers often claim another engine or version |
| More consistency checks. Graphics names against the platform and WebGPU, two media APIs against each other, impossible window sizes, a phone with no touch, fonts from another operating system | P1 | M | Spoofing tools tend to change one API at a time |
| Video decoding capabilities. Whether common codecs decode efficiently, read without any DRM query | P2 | S | Shows hardware video decoders, a real device trait |
| More rendering signals, reported. System colours, font smoothing, SVG text sizes, a small GPU maths drawing, screen orientation | P2 | M | Different engine paths give different, mostly stable, results |
| Compare two canvas types. Draw the same scene on a normal and an offscreen canvas | P2 | S | Extensions often patch only one of them |
| Optional worker check. Only if the site provides a worker file of its own: read key values again inside it and compare | P2 | M | A worker is harder for extensions to fake; the site makes the request, so WhiskerJS still makes none |
| Version-free variants. Browser and driver versions also reported without minor numbers | P2 | S | Lets servers compare devices across automatic updates |
| Optional server package. A separate open-source package: group-by-group matching that treats very common setups as a crowd rather than one device, header checks, the JA4 TLS fingerprint, and open IP lists | P1 | L | Matching quality is decided on the server; checks across network and browser layers catch scripts that pretend to be browsers |
| Published test protocol first. Write down how we will measure uniqueness and stability before collecting any data, including identical twin devices | P1 | M | Published figures vary a lot with who is measured; a fixed protocol makes ours comparable and honest |
| Real devices, including iPhones. Nightly runs on a device farm and real Safari | P2 | M | Every iPhone browser outside the EU uses WebKit, and test WebKit is not Safari |
| Packaging. Import only the collectors you need; a single script file for script tags; checked types | P2 | M | Smaller pages for sites that need fewer signals |
| First package release (0.x). Once the repository is public, published with provenance from CI | P1 | S | Lets anyone verify that the package was built from this source |
| A page per signal with measured figures. What each signal reads, how stable and distinctive it measured, and what it costs | P2 | M | Makes the trade-offs open to inspection |
| Item | Priority | Effort | Why, in plain words |
|---|---|---|---|
| Learned signal weights in the server package | P2 | M | Weight signals by how stable and distinctive they measured, once there is data |
| Faster matching for large sites | P2 | M | Published linking methods slowed down and lost accuracy at scale |
| Replay test against the anti-replay binding | P2 | M | Research showed that copied browser values can fool matchers |
| A public, opt-in test page | P3 | L | Only with explicit consent, digests only, no IP addresses kept and an ethics review |
| HTTP/2 and later network-level checks in the server package | P2 to P3 | M to L | Separate real browsers from HTTP libraries |
| Small extra signals (CSS support list, passkey capabilities, Global Privacy Control and others) | P3 | S each | Little new information each; useful for consistency |
| GPU timing research module, opt-in and off by default | P3 | L | Can tell identical hardware apart, but costs seconds of GPU work |
| Framework wrappers (React first) | P3 | M | Easier adoption once the core settles |
| External security audit before 1.0 | P2 | M | An outside view of tampering and replay risks |
| What | Why not |
|---|---|
| WebRTC, media device lists, network information, battery, geolocation | They can trigger network activity, prompts or permission-linked data |
| Storage quota | Browsers now report it in a way that tells little, and it changes with use |
| DRM key system queries, payment checks, installed related apps | They can show browser UI, fetch files or reveal installed apps |
| Starting sensors, screen details or any device chooser | They prompt the user; only “does this API exist” is read |
| Gamepad values | Empty until the user presses a button, by design |
| Detecting DevTools through the console | Needs console output, and stopped working in 2025 |
| Workers the library builds itself | A page’s security policy can block them and report the block over the network |
| Timing tricks inside the id | Too unstable from run to run |
| Detecting installed extensions | Too invasive for a device collector |
| Mouse and keyboard behaviour in the core | Outside a device collector |
| Parsing the User-Agent in the browser | Belongs on the server |
| JA3 | Chrome shuffles the data it is computed from, so it changes per connection |
| The JA4+ family beyond JA4 | Its licence allows non-commercial use only |
| Scores, verdicts or stored ids in the browser | WhiskerJS collects; the site’s server decides |
All sources were read on 2026-10-26. Some were available only as an
abstract or a summary; the full roadmap in the library repository
(docs/ROADMAP.md) marks which.
cf properties