avatarsniff

Sniff out generic default avatars, straight from the pixels.

Documentation

A lot of users never set a profile picture, so providers serve a boring auto-generated default: an initial on a coloured square (Google), the Gravatar mystery-person, or a solid placeholder. avatarsniff reads the pixels and catches those, so you can swap in something better instead of the generic one. Decodes PNG/JPEG/GIF/WEBP/SVG up to 10MB. Framework-agnostic, zero dependencies, server and client.

Install

pnpm add avatarsniff

Try it

This runs entirely in your browser through sniff. Drop your own avatar in, or click any of the real samples below to classify it on the spot.

…or click a sample below to classify it as if you'd uploaded it.

Browser

browser.ts
import { sniff } from "avatarsniff";

const ctx = canvas.getContext("2d");
ctx.drawImage(avatarImg, 0, 0, 64, 64);

const { isDefault, matched, reason } = await sniff(
  ctx.getImageData(0, 0, 64, 64)
);

if (isDefault) {
  // generic provider default (matched: "initials" | "identicon" | ...)
  // swap in your own avatar
}

Server (Node)

Batteries included, and still zero-dependency: every decoder is bundled into the build, so PNG, GIF and JPEG decode in pure JS on any runtime. For WEBP and SVG in plain Node, opt into a subpath (import "avatarsniff/webp" or import "avatarsniff/svg") so the wasm only loads when you actually need it. It rejects anything over 10MB before decoding.

server.ts
import { sniff } from "avatarsniff";

// one entry point - pass bytes, a URL, or canvas pixels
// decodes PNG/JPEG/GIF/WEBP/SVG, up to 10MB, zero deps
const result = await sniff(bytes); // Uint8Array | ArrayBuffer

const fromUrl = await sniff(user.photoUrl); // fetched for you
// null if the URL is missing or the fetch fails

// every detector family is on by default - opt out per call
await sniff(bytes, { detect: { identicon: false } });

How it decides

It keys on structure, never a hard-coded palette, so it keeps working as providers add new colours. An image counts as a default when the background is one flat colour that's neither near-white nor near-black, a small near-white glyph (the initial) sits on top, and there's almost no other coloured content.