npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

stable-fingerprint

v0.1.5

Published

Lightweight browser fingerprinting library that produces a stable visitor identifier and a confidence score.

Readme

stable-fingerprint

Identify a returning browser with no cookies and no storage - just the device underneath it.

CI npm npm downloads license

Browsers leak a surprising amount about the machine underneath them: the GPU, the installed fonts, the audio stack, the screen. stable-fingerprint reads those signals on the client, hashes the stable ones into a visitorId, and tells you how confident it is. No cookies, no localStorage, nothing to clear.

import Fingerprint from 'stable-fingerprint';

const fp = await Fingerprint.load();
const { visitorId, confidence } = await fp.get();
// visitorId  -> "5d01727c86dcd5b7e4d6d6ecbd8b3382"
// confidence -> { score: 0.99 }

The id is the same in a normal window and an incognito window, survives browser zoom, and survives browser auto-updates. Try the live demo, or run npm run demo locally for a per-signal debug view.

Why it stays stable

Every signal has a role, and only the stable ones build the id:

| Role | Signals | In the id? | | ---------- | ----------------------------------------------------------- | ---------- | | core | fonts, screen, timezone, languages, platform, plugins, math | yes | | volatile | canvas, WebGL, audio, hardware | no | | report | media preferences, incognito guess | no |

Privacy browsers (Firefox private windows, Brave) inject random noise into canvas, WebGL and audio to defeat fingerprinting, and under-report CPU cores. Those are the volatile signals: they still feed the confidence score, but keeping them out of the id is exactly why it survives private mode. The UA is version-normalized and devicePixelRatio is dropped, so updates and zoom don't move the id either.

Install

npm install stable-fingerprint

API

load(options?) prepares an agent; call it early so the first get() is fast.

  • options.sources - override the built-in signal registry
  • options.timeout - per-source timeout in ms (default 1000)

agent.get() returns:

interface FingerprintResult {
	visitorId: string; // 32-char hex hash
	confidence: { score: number };
	components: Record<string, { value; duration } | { error; duration }>;
	version: string;
}

Under the hood the id is a MurmurHash3 x64 128-bit hash of the core components, serialized deterministically (object keys sorted) so ordering never affects it.

Scope

This is a per-browser fingerprint. Same browser gives the same id across sessions, incognito, zoom and updates. Different browsers or devices are treated as different identities, which is correct: even Math.atanh returns a different last digit across JavaScript engines. Linking across browsers or devices needs server-side signals (IP, TLS/JA4) that a client library cannot see.

Development

npm i && npm test   # the whole dev loop
npm run demo        # interactive demo
npm run build       # ESM + CJS + types

Roadmap

  • [x] Stable per-browser id with confidence scoring
  • [x] Randomization-proof core, resilient to private mode
  • [x] Interactive demo with a per-signal debug view
  • [ ] Optional persistence layer for faster repeat lookups
  • [ ] Server-side signal (IP) for cross-browser linking

Releasing

Publishing is automated with GitHub Actions using npm trusted publishing (OIDC), so no npm token is stored in the repo. One-time setup on npmjs.com: on the package's Settings -> Trusted Publishers, add GitHub Actions with this repository and the workflow file release.yml.

npm version patch      # bump the version, commit, and create a v* tag
git push --follow-tags # triggers the workflow, which publishes to npm

Pushes to main never publish - only version tags do.

License

MIT