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

favipulse

v1.1.0

Published

Tiny (<3 KB gz), zero-dependency, typed favicon library: deterministic generative/identicon favicons from a seed with self-drawing animation, badges, progress rings and pulse, plus per-environment (dev/staging/prod) icons. CLI generates svg/ico/png sets +

Readme

favipulse

npm CI Node license: MIT size zero deps types

Favicons that say something: who, where, and what's happening.

Live demo → · Docs (wiki) · Migrating from favico.js / tinycon

favipulse draws a deterministic generative favicon from any seed (with an optional self-drawing animation), shows badges, a progress ring for builds and uploads and a pulse when something needs attention, and gives dev, staging and prod their own icon so you never ship from the wrong tab again. A CLI and Vite / Next.js helpers generate static favicon.svg, favicon.ico, PNG sizes and a manifest snippet per environment.

Under 3.1 KB min+gzip, zero dependencies, TypeScript types, ESM + CJS, SSR-safe, works without canvas (pure SVG) and respects prefers-reduced-motion. A modern, maintained replacement for favico.js (~43k downloads/week, last release 2015) and tinycon (~11k/week, last release 2017).

Install

npm i favipulse

Or from a CDN, no build step: <script src="https://unpkg.com/favipulse"></script> → window.favipulse.

Usage

import { favipulse } from 'favipulse';

const fav = favipulse({ seed: 'acme', env: 'auto' }); // dev/staging/prod detected from the hostname
await fav.draw();          // self-drawing intro (instant with reduced motion)

fav.badge(3);              // unread count; 0 hides, >99 shows 99+
fav.badge('!');            // or short text
fav.progress(0.42);        // progress ring (0-1 or 0-100)
fav.progress(null);        // hide it
fav.pulse();               // pulsing halo; fav.pulse(false) to stop
fav.reset();               // clear badge, progress, pulse
fav.destroy();             // restore the original favicon

Progress during an upload

const fav = favipulse();
xhr.upload.onprogress = (e) => fav.progress(e.loaded / e.total);
xhr.onloadend = () => fav.progress(null).badge('✓');

Static favicon sets per environment (CLI)

npx favipulse generate --seed acme --out public/favicons
# ✔ dev      10 files → public/favicons/dev/
# ✔ staging  10 files → public/favicons/staging/
# ✔ prod     10 files → public/favicons/prod/

Each folder holds favicon.svg, favicon.ico (16/32/48), favicon-16x16.png, favicon-32x32.png, favicon-48x48.png, apple-touch-icon.png (180), icon-192.png, icon-512.png, manifest.webmanifest and a head.html snippet to paste into <head>. PNGs are rasterised in pure JS, so no sharp, canvas or ImageMagick needed.

| Option | Description | |---|---| | --seed <text> | Seed for the icon, e.g. your app name (required) | | --env <list> | Comma-separated environments (default dev,staging,prod) | | --out <dir> | Output directory, one sub-folder per environment (default favicons) | | --flat | With a single --env, write straight into --out | | --style grid\|dots\|rings | Pattern style (default: per-environment preset) | | --hue, --fg, --bg, --radius | Colour and shape overrides | | --name, --base | Manifest name and URL prefix for snippets | | --small | Only svg, ico and 16/32/48 PNGs | | --json | Machine-readable summary |

npx favipulse svg --seed acme --env dev prints a single SVG to stdout.

Vite

// vite.config.js
import favipulse from 'favipulse/vite';
export default { plugins: [favipulse({ seed: 'acme' })] };

Emits the set for the current mode (development → dev, staging → staging, otherwise prod), serves it in dev, and replaces the <link rel="icon"> in index.html. Pass env: (mode) => ... to map your own modes and dir: 'icons' for a sub-folder.

Next.js (App Router)

// app/layout.tsx
import { faviconMetadata } from 'favipulse/next';
export const metadata = { icons: faviconMetadata({ seed: 'acme' }) }; // env from VERCEL_ENV / NEXT_PUBLIC_APP_ENV / NODE_ENV

// or app/icon.svg/route.ts
import { iconRoute } from 'favipulse/next';
export const GET = iconRoute({ seed: 'acme' });

For live badges in a client component: useEffect(() => { const f = favipulse({ seed: 'acme' }); f.badge(count); return () => f.destroy(); }, [count]).

API

favipulse(options?) → Favipulse

| Option | Default | Description | |---|---|---| | seed | location.host | Any string. Same seed, same icon, everywhere | | env | — | 'dev', 'staging', 'prod', a custom name, or 'auto' (from hostname) | | style | 'grid' | 'grid' (mirrored identicon), 'dots', 'rings' | | hue / fg / bg / radius | from seed | Colour overrides; bg: 'transparent' for no tile | | badge, badgeBg, badgeFg | — / #ef4444 / #fff | Initial badge and colours | | progress, ringColor | — / icon colour | Initial progress and ring colour | | pulse | false | Start pulsing immediately | | stripe | env preset | Colour of the environment stripe | | format | 'svg' | 'svg', 'png' (canvas, falls back to SVG), 'auto' (PNG only on Safari) | | size | 64 | PNG size when using canvas | | reducedMotion | true | Honour prefers-reduced-motion: reduce | | fps | 12 | Animation frame rate (favicons don't need 60) | | document | global | Target document (iframes, tests) |

Methods (chainable): badge(v, colours?), progress(v), pulse(on = true), draw(ms = 900): Promise, set(options), reset(), destroy(). Properties: svg (current markup), link (the managed <link>).

Pure helpers (no DOM, safe in Node / workers / SSR)

| Export | Description | |---|---| | icon(seed, opts?) | Deterministic icon model (shapes in a 64×64 box) | | toSVG(icon, overlay?) | SVG markup with optional badge, progress, pulse, reveal, stripe | | dataURI(svg) | data:image/svg+xml,... for <link rel="icon"> | | envIcon(seed, env), envSVG(seed, env) | Environment variants; presets in ENVS (add your own) | | detectEnv(hostname?) | localhost/*.local/*.test → dev; staging./preview/*.vercel.app/*.netlify.app/*.pages.dev → staging; else prod | | hash, rng, hsl, badgeText, svgToPNG | Building blocks |

favipulse/node: generate(options) → { [file]: Buffer | string }, writeSet(dir, files), rasterize, encodePNG, encodeICO. favipulse/compat: Favico and Tinycon drop-in shims.

Compared

| | favipulse | favico.js | tinycon | |---|---|---|---| | Last release | 2026 | 2015 (0.3.10) | 2017 (0.6.8) | | Size (min+gzip) | 2.9 KB (more features) | 3.0 KB | 1.5 KB | | TypeScript types | ✔ built in | ✖ | ✖ | | ESM + CJS | ✔ | ✖ UMD | ✖ UMD | | Works without canvas / SSR-safe | ✔ SVG | ✖ | ✖ | | Badges | ✔ | ✔ | ✔ | | Progress ring | ✔ | ✖ | ✖ | | Generated icon from a seed | ✔ + self-drawing | ✖ | ✖ | | Per-environment icons | ✔ | ✖ | ✖ | | prefers-reduced-motion | ✔ | ✖ | n/a | | Static svg/ico/png + manifest CLI | ✔ | ✖ | ✖ | | Vite / Next.js helpers | ✔ | ✖ | ✖ | | Images, video, webcam in favicon | ✖ | ✔ | ✖ |

Migrating from favico.js / tinycon

Quickest path: swap the import for the shim, then refactor at leisure.

// favico.js
import { Favico } from 'favipulse/compat';
const favicon = new Favico({ animation: 'pop', bgColor: '#5CB85C' });
favicon.badge(12); favicon.reset();

// tinycon
import { Tinycon } from 'favipulse/compat';
Tinycon.setOptions({ background: '#549A2F' }); Tinycon.setBubble(6);

| favico.js | favipulse | |---|---| | new Favico(opts) | favipulse({ badgeBg, badgeFg }) | | .badge(n) | .badge(n) | | .reset() | .reset() | | animation: 'pop' \| 'fade' \| ... | .pulse() (reduced-motion aware) | | .image(), .video(), .webcam() | not supported (use a seed icon) |

| tinycon | favipulse | |---|---| | Tinycon.setBubble(n) | fav.badge(n) | | Tinycon.setOptions({ background, colour }) | favipulse({ badgeBg, badgeFg }) | | fallback: 'force' (title) | compat shim keeps it; core never needs a fallback | | Tinycon.reset() | fav.reset() |

Full guide: wiki/Migrating from favico.js and tinycon.

Browser support

SVG favicons: Chrome, Edge, Firefox, Opera. Safari doesn't render SVG favicons, so use format: 'auto' (PNG via canvas on Safari) and ship the static favicon.ico from the CLI as the baseline. Without canvas (jsdom, locked-down contexts) favipulse keeps the SVG.

Limitations

Browsers throttle favicon updates in background tabs, so animations are best-effort there. Badges use the system font, so glyphs vary slightly by OS. The deterministic pattern uses 15 bits, so with thousands of seeds you'll see repeats; colour and style make collisions unlikely in practice.

Releasing

Tag vX.Y.Z and publish a GitHub release: release.yml tests, then publishes to npm with provenance (enabled by the NPM_PUBLISH repository variable) and to GitHub Packages as @mrlouiswright/favipulse.

Support

If this project is useful to you, you can support its development with a donation: Buy me a coffee. It helps keep these tools maintained, documented and free for everyone.

Optional credit link

MIT needs no attribution. If you want a small backlink in the page:

favipulse({ seed: 'acme', credit: true });
// or: creditTarget: '#footer' / an HTMLElement

Style it yourself (no inline styles are set):

.favipulse-credit { font-size: 0.75rem; opacity: 0.65; }

destroy() removes the link. Off by default; no network calls or tracking.

Credit (optional)

Free to use under MIT, no attribution required. If favipulse helps you, a link back is appreciated:

<a href="https://louiswright.co.uk">Built with favipulse by Louis Wright</a>
[![Built with favipulse by Louis Wright](https://img.shields.io/badge/built%20with-favipulse-blue)](https://louiswright.co.uk)

License

MIT © Louis Wright (Louis Wright)