All projects

Work · 2026

avatarGenerator

A name in, an avatar out — deterministic, offline, and free of every dependency.

LiveSource

At a glance

  • Zero dependencies — no npm package, no CDN, no network request
  • Deterministic via NFC normalisation, an FNV-1a hash and a Mulberry32 PRNG
  • Three styles — character, abstract, initials
  • Billions of possible combinations in the character style

The problem

Avatars for users without an uploaded picture are usually solved with Gravatar or a service like DiceBear. Both mean one external request per avatar, one IP address handed to a third party, and a dependency you do not control. For a site that otherwise sends no bytes anywhere, that is an absurd price for a 128×128 image.

The approach

The generator computes the image itself, from nothing but the name.

The chain is short and fully deterministic. The name is normalised to NFC, so that “José” hashes identically whether it arrived NFC- or NFD-encoded. An FNV-1a hash turns that into a 32-bit seed. The seed initialises a Mulberry32 PRNG which then draws — always in the same order — every visual trait: skin tone, hairstyle, hair colour, eyes, brows, nose, mouth, beard, clothing, glasses, headwear, earring, freckles, background gradient.

Same name → same seed → same call sequence → identical image. On any device, in any session, with nothing stored anywhere.

import { generateAvatar } from './avatarGenerator.js';

const svg = generateAvatar('John Doe', { size: 128 });
document.getElementById('avatar').innerHTML = svg;

What made it interesting

The hard part was not the randomness, it was the face. A circle with two dots reads as a placeholder. The head is therefore built from Bézier curves, with consistent outlines, soft shading and a subtle sticker shadow — so that ten avatars side by side look like a set rather than ten unrelated random results.

The second non-obvious constraint is the order of the PRNG calls. It is part of the public behaviour: inserting a new trait in the middle of the chain shifts every subsequent draw, and therefore changes every avatar that already exists. New traits belong at the end.

Integration

generateAvatar() returns a complete SVG string including xmlns, ready to drop into innerHTML or a template. For DI-style architectures there is also a thin AvatarGenerator class that holds default options in its constructor and otherwise just delegates.

The live demo below runs on exactly that file — not on a reimplementation.

Live demo

The generator runs right here in your browser — no server, no network request. Same name, same avatar, every time.

The live demo needs JavaScript. The source is linked either way.