- SOTA Embedding Retrieval: Gemini + pgvector for Production Chat
- A Review of Agentic Design Patterns
- Model Context Protocol (MCP) and MCP Servers in LLM Agent Systems
- Building AI Agents for Automated Multi-Format Content: From News to Podcasts
- Rediscovering Cursor
- GraphRAG > Traditional Vector RAG
- Cultural Bias in LLMs
- Mapping out the AI Landscape with Topic Modelling
- Sustainable Cloud Computing: Carbon-Aware AI
- Defensive Technology for the Next Decade of AI
- Situational Awareness: The Decade Ahead
- Mechanistic Interpretability: A Survey
- Why I Left Ubuntu
- Multi-Agent Collaboration
- Embeddings and Vector Databases: Enhancing Retrieval Systems
- Building an Automated Newsletter-to-Summary Pipeline with OpenAI: Zapier AI Actions vs AWS SES & Lambda
- Local AI Image Generation
- MLOps: Deploying a Distributed Ray Python Server with Kubernetes, EKS & KubeRay
- Making the Switch to Linux for Development: A Developer's Experience
- Scaling Options Pricing with Ray
- The Async Worker Pool
- ›Browser Fingerprinting: Introducing My First NPM Package
- Reading Data from @socket.io/redis-emitter without Using a Socket.io Client
- Socket.io Middleware for Redux Store Integration
- Sharing TypeScript Code Between Microservices: A Guide Using Git Submodules
- Efficient Dataset Storage: Beyond CSVs
- Embracing Next.js 13: Why I switched from Plain React
- Deploy & Scale Socket.io Containers in ECS with Elasticache
- Implementing TOTP Authentication in Python using PyOTP
- Simplifying Lambda Layer ARNs and Creating Custom Layers in AWS
- TimeScaleDB Deployment: Docker Containers and EC2 Setup
- How to SSH into an EC2 Instance Using PuTTY
This post shares my debut into the world of NPM packages-a zero dependencies package exporting a swift and synchronous function to compute a browser fingerprint. No user permissions or cookie storage is needed either!
See the details on NPM or check out the source code on Github.
Device Fingerprinting
In the age of the internet, accurately identifying visitors is paramount for websites, whether it's to serve personalized content or keep malicious actors at bay. Although cookies have been the traditional heroes, their limitations-like incompatibility with incognito modes and multi-browser usage-led to the invention of device fingerprinting.
Often referred to as machine fingerprint, browser fingerprint, and several other names, a device fingerprint collects specific information about an online device, ensuring its unique identification during future visits. This form of identification remains robust, even when users disable cookies or other tracking tools.
The essence of browser fingerprinting lies in capturing a unique combination of a user's web browser and device details, creating a distinctive 'digital fingerprint'. For example, while many visitors to a website may have the same model of iPhone, the software and drivers installed, geo-location, languages, browser and OS version, and even minute variances in the hardware could be different.
Browser fingerprinting techniques gather one or more of these signals and aim to capture these minor variances between users. Such fingerprints are resilient, remaining consistent across incognito sessions or when users are using VPNs.
The Data Behind Browser Fingerprinting
The depth of information that browser fingerprinting can mine is truly staggering. From device models, operating system versions, and browser specifications to intricate details like user timezone, preferred language settings, and ad blocker usage, the scope is vast.
The function used in my package amalgamates many of these signals. Below is a screenshot of some of the data I decided to use in my NPM package to generate a fingerprint:

For a closer look, explore the deployed Next app here.
Be careful: The strongest discriminating factor is canvas token which can't be computed on old devices (e.g. iPhone 6)
This data is then run through a MurmurHash3 hashing algorithm to generate a unique fingerprint for each browser.
Singleton Pattern: Efficient and Effective
For the creation of the fingerprint, I've opted for the singleton design pattern. This ensures that the class is instantiated only once. On successive calls to the fingerprint function, the very same instance returns, facilitating faster caching of fingerprint data and swifter function calls.
This approach has proven effective, especially when paired with API calls to authenticate that the user making a request was the same user who logged in and was issued the JWT.
A Quick Tip on Hydration Errors
To avoid the hydration errors we get when using checks like typeof window !== 'undefined'
in the logic, use the following approach:
Import the fingerprintBrowser
function from the package:
import { getBrowserFingerprint } from 'fingerprint-browser';
Next, make use of it within a useEffect
hook:
const [browserFingerprint, setBrowserFingerprint] = useState('');
useEffect(() => {
setBrowserFingerprint(fingerprintBrowser());
}, []);
This approach ensures that server-side rendering remains unaffected as hooks aren't executed. Wrapping the window usage inside a useEffect
triggered on mount ensures the client executes it post hydration.
Conclusion
Browser fingerprinting is an innovative, resilient, and essential tool for the modern web. I'm happy to have contributed to this domain, and I invite developers and enthusiasts to explore and provide feedback on my package.
References and Special Thanks
- Thanks to
@damianobarbati
for get-browser-fingerprint which provided some inspiration - Special thanks to Valentin Vasilyev for the original
fingerprintjs
slightly modified - Thanks to Open Source Device Fingerprinting by Dark Wave Tech for the various identity functions
- Dave Alger for his fingerprinting CodePen
- N8Brooks on Github for his implementation of unsigned 32-bit MurmurHash3