facedye

Deterministic gradient avatars generated from any string. Same name in, same avatar out — no images to store, no network requests. Pure SVG in three styles, with React bindings included.

mayaleoineskofisana

Install

bash
pnpm add facedye

React

The component renders an img backed by a data URI, so it needs no CSS from your app and works in server components. Pass any stable string as name — a user id, email, or username.

tsx
import { GradientAvatar } from "facedye/react";

export function UserBadge({ user }) {
  return <GradientAvatar name={user.id} variant="glossy" size={96} />;
}

Core

The root export is framework-agnostic and dependency-free — use it in Node, edge runtimes, Vue, or plain HTML.

ts
import { buildAvatarSvg, avatarDataUri } from "facedye";

// raw <svg> markup — save it, inline it, or serve it
const svg = buildAvatarSvg("maya", { variant: "punchy", radius: "squircle" });

// data URI for <img src>, css backgrounds, favicons
const uri = avatarDataUri("maya", { size: 256 });

Options

optionvaluesdefault
variantsoft · punchy · glossyglossy
radiusnone · sm · md · lg · xl · 2xl · 3xl · squircle · fullfull
sizeany pixel value512

URL API

This site also serves avatars over HTTP — same engine, same options as query params. Handy for READMEs and places you can't run JavaScript.

html
<img
  src="/api/avatar/maya?variant=glossy&radius=full&size=256"
  width="64"
  height="64"
  alt="maya"
/>

Pixel

For a retro look, the same hash can drive a pixel sprite instead of a gradient. Cells roll across a 8×8 grid and mirror down the middle — the symmetry is what makes random noise read as a little creature. Three greys give it depth, and the same name always yields the same face.

mayaleoineskofisana

PixelAvatar takes radius and size — there's no variant, since the palette is fixed.

tsx
import { PixelAvatar } from "facedye/react";

export function UserBadge({ user }) {
  return <PixelAvatar name={user.id} radius="squircle" size={96} />;
}

Outside React, buildPixelAvatarSvg returns the raw markup and pixelAvatarDataUri the src-ready string — same dependency-free core as the gradient.

ts
import { buildPixelAvatarSvg, pixelAvatarDataUri } from "facedye";

// mirrored 8x8 sprite as standalone <svg> markup
const svg = buildPixelAvatarSvg("maya", { radius: "squircle" });

// data URI for <img src>, css backgrounds, favicons
const uri = pixelAvatarDataUri("maya", { size: 256 });
optionvaluesdefault
radiusnone · sm · md · lg · xl · 2xl · 3xl · squircle · fullfull
sizeany pixel value512

And over HTTP, from its own endpoint:

html
<img
  src="/api/pixel/maya?radius=full&size=256"
  width="64"
  height="64"
  alt="maya"
/>

Loaders

The same pixel-grid idea, animated. Spindle is a loading spinner woven from a grid of cells that pulse as a wave travels across them. It injects its own keyframes, needs no CSS from your app, and respects prefers-reduced-motion.

LoadingLoadingLoadingLoadingLoading

It lives on its own /loaders entry so the animation code stays out of bundles that only need avatars. Every prop is optional.

tsx
import { Spindle } from "facedye/loaders";

export function Saving() {
  return <Spindle pattern="snake" animationStyle="flip" />;
}
optionvaluesdefault
patterndiagonal · snake · ripple · diamond · radar · spiral · orbit · …diagonal
animationStylepulse · breathe · pop · blink · glow · flip · squash · spinpulse
size / rows / colsgrid dimensions4
speedseconds per cycle0.75

See all patterns and animation styles running live on the loaders page.

Lower level

The deterministic internals are exported too, if you want to build a custom renderer on the same palette.

ts
import {
  hashName,
  generateGradient,
  VARIANT_STYLES,
  RADIUS_RATIOS,
} from "facedye";

// 53-bit deterministic hash pair behind every avatar
const [high, low] = hashName("maya");

// the palette and blob layout, if you want to render it yourself
const { base, baseHue, blobs } = generateGradient("maya", VARIANT_STYLES.glossy);