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

@theseed.dev/fingerprint

v1.1.0

Published

Fingerprinting navigateur léger, sans dépendance — Canvas, Audio, WebGL, Fonts, Media Devices. Compatible Vue 3, React, Next.js, Nuxt, Angular.

Readme

Génère un identifiant visiteur unique (visitorId) à partir de 7 signaux matériels et logiciels du navigateur — sans cookies, sans localStorage, sans serveur.

npm install @theseed.dev/fingerprint

Signaux collectés

| Signal | Technique | Sensibilité | |---|---|---| | Canvas | Rendu 2D texte + formes | GPU, pilotes, anti-aliasing | | Audio | OfflineAudioContext oscillateur + compresseur | Carte son, pilotes audio | | WebGL | Extension WEBGL_debug_renderer_info | Vendor GPU, renderer string | | Fonts | Détection de largeur Canvas (25 familles) | Polices installées | | Media Devices | enumerateDevices() — types uniquement | Micros, caméras présents | | Environnement | Screen, langue, timezone, CPU, RAM… | Profil OS / navigateur | | Plugins | navigator.plugins | Extensions installées |

Aucune permission utilisateur requise. Aucune donnée ne quitte le navigateur.


Démarrage rapide

import { generateFingerprint } from "@theseed.dev/fingerprint";

const result = await generateFingerprint();

console.log(result?.visitorId);   // "1a2b3c4d5e6f"
console.log(result?.components);  // { canvas, audio, webgl, fonts, ... }
console.log(result?.generatedAt); // 1716638400000

Retourne null si exécuté hors navigateur (SSR, Node.js, Deno…).


Intégrations par framework

Vanilla JS / TypeScript

import { generateFingerprint } from "@theseed.dev/fingerprint";

const result = await generateFingerprint();
if (result) {
  document.title = `Visitor: ${result.visitorId}`;
}

React

Hook personnalisé useFingerprint :

// hooks/useFingerprint.ts
import { useEffect, useState } from "react";
import { generateFingerprint, FingerprintResult } from "@theseed.dev/fingerprint";

export function useFingerprint() {
  const [result, setResult]   = useState<FingerprintResult | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError]     = useState<Error | null>(null);

  useEffect(() => {
    generateFingerprint()
      .then(setResult)
      .catch(setError)
      .finally(() => setLoading(false));
  }, []);

  return { visitorId: result?.visitorId ?? null, components: result?.components ?? null, loading, error };
}
// App.tsx
import { useFingerprint } from "./hooks/useFingerprint";

export default function App() {
  const { visitorId, loading } = useFingerprint();

  if (loading) return <p>Identification…</p>;
  return <p>Visitor ID : <code>{visitorId}</code></p>;
}

Vue 3

Le composable useFingerprint() est intégré directement dans le package.
Il génère le fingerprint au montage du composant et expose un état réactif.

<!-- components/VisitorBadge.vue -->
<script setup>
import { useFingerprint } from "@theseed.dev/fingerprint";

const { visitorId, loading, error } = useFingerprint();
</script>

<template>
  <p v-if="loading">Identification en cours…</p>
  <p v-else-if="error" class="error">Erreur : {{ error.message }}</p>
  <p v-else>Visitor ID : <code>{{ visitorId }}</code></p>
</template>

Next.js — App Router

Les Browser APIs ne sont disponibles que côté client. Utilise la directive "use client".

// components/VisitorTracker.tsx
"use client";

import { useEffect, useState } from "react";
import { generateFingerprint } from "@theseed.dev/fingerprint";

export function VisitorTracker() {
  const [visitorId, setVisitorId] = useState<string | null>(null);

  useEffect(() => {
    generateFingerprint().then((r) => r && setVisitorId(r.visitorId));
  }, []);

  return visitorId ? <span data-visitor={visitorId} /> : null;
}

Next.js Pages Router :

// pages/index.tsx
import dynamic from "next/dynamic";

// Désactive le SSR — generateFingerprint() retourne null côté serveur
const VisitorTracker = dynamic(() => import("../components/VisitorTracker"), {
  ssr: false,
});

Nuxt 3

<!-- components/VisitorTracker.vue -->
<script setup>
// ClientOnly garantit que le code s'exécute uniquement dans le navigateur
const visitorId = ref(null);

onMounted(async () => {
  const { generateFingerprint } = await import("@theseed.dev/fingerprint");
  const result = await generateFingerprint();
  if (result) visitorId.value = result.visitorId;
});
</script>

<template>
  <span v-if="visitorId" :data-visitor="visitorId" />
</template>

Ou avec le composant <ClientOnly> de Nuxt :

<!-- app.vue -->
<template>
  <ClientOnly>
    <VisitorTracker />
  </ClientOnly>
</template>

Angular

// services/fingerprint.service.ts
import { Injectable } from "@angular/core";
import { from, of } from "rxjs";
import { catchError } from "rxjs/operators";
import { generateFingerprint } from "@theseed.dev/fingerprint";

@Injectable({ providedIn: "root" })
export class FingerprintService {
  readonly visitorId$ = from(generateFingerprint()).pipe(
    catchError(() => of(null))
  );
}
// components/visitor-badge.component.ts
import { Component } from "@angular/core";
import { AsyncPipe } from "@angular/common";
import { FingerprintService } from "../services/fingerprint.service";

@Component({
  selector: "app-visitor-badge",
  standalone: true,
  imports: [AsyncPipe],
  template: `
    <code>{{ (fp.visitorId$ | async)?.visitorId }}</code>
  `,
})
export class VisitorBadgeComponent {
  constructor(readonly fp: FingerprintService) {}
}

Structure du résultat

interface FingerprintResult {
  /** Hash 53-bit en hexadécimal (ex: "1a2b3c4d5e6f") */
  visitorId:   string;
  /** Timestamp de génération (ms depuis epoch) */
  generatedAt: number;
  /** Composants bruts ayant servi au calcul */
  components:  FingerprintComponents;
}

interface FingerprintComponents {
  // Signaux graphiques
  canvas:  string | null; // Data URL canvas rendu
  webgl:   string | null; // "Vendor ~ Renderer"

  // Signal audio
  audio:   string | null; // "<hash>-<sum>"

  // Signaux environnement (v1.1+)
  fonts:              string[];      // Polices détectées
  mediaDevices:       string | null; // Types de devices (audioinput,videoinput…)
  plugins:            string[];      // navigator.plugins

  // Environnement navigateur
  userAgent:           string;
  language:            string;
  platform:            string;
  screenResolution:    string;   // "1920x1080"
  colorDepth:          number;
  timezone:            string;   // "Europe/Paris"
  touchSupport:        boolean;
  hardwareConcurrency: number;
  deviceMemory?:       number;   // Go de RAM (approximatif)

  // Métadonnées navigateur supplémentaires (v1.1+)
  cookiesEnabled:  boolean;
  doNotTrack:      string | null;
  sessionStorage:  boolean;
  localStorage:    boolean;
  indexedDB:       boolean;
  openDatabase:    boolean;
  cpuClass:        string | null;
  oscpu:           string | null;
  vendor:          string;
  vendorSub:       string;
  productSub:      string;
  navigatorOnline: boolean;
}

API

generateFingerprint()

function generateFingerprint(): Promise<FingerprintResult | null>

Collecte tous les signaux en parallèle (Promise.all) et retourne le résultat.
Retourne null si window n'est pas défini (Node.js, SSR…).

useFingerprint() (Vue 3 uniquement)

function useFingerprint(): {
  visitorId:  Ref<string | null>;
  components: ShallowRef<FingerprintComponents | null>;
  loading:    Ref<boolean>;
  error:      ShallowRef<Error | null>;
}

Composable Vue 3 — génère le fingerprint au montage (onMounted).
Lance une exception si appelé hors d'un projet Vue 3.


Algorithme de hash

Le visitorId est généré par cyrb53 — un hash non-cryptographique 53-bit conçu pour minimiser les collisions sur de petits jeux de données :

visitorId = cyrb53(JSON.stringify(components)).toString(16)
  • Déterministe : même machine + même navigateur → même visitorId
  • Sensible : un changement de signal = visitorId différent
  • Rapide : O(n) sur la longueur de la chaîne sérialisée

⚠️ Ce n'est pas un hash cryptographique. Ne pas utiliser pour la sécurité.


Notes de confidentialité

Ce module collecte des informations purement techniques sur le navigateur — aucune donnée personnelle identifiable (nom, email, adresse IP).

  • ✅ Aucun cookie créé
  • ✅ Aucune écriture dans le localStorage
  • ✅ Aucune requête réseau
  • ✅ Aucune permission demandée à l'utilisateur
  • ⚠️ Pour mediaDevices : seuls les types de devices sont collectés (pas les labels, qui nécessiteraient une permission)

Conformité légale : selon ton usage (analytics, anti-fraude, personnalisation), tu peux être soumis au RGPD, CCPA ou autres réglementations. Assure-toi de mentionner la collecte d'empreinte dans ta politique de confidentialité.


Powered by theseed.dev — License MIT