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.
pnpm add facedyeThe 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.
import { GradientAvatar } from "facedye/react";
export function UserBadge({ user }) {
return <GradientAvatar name={user.id} variant="glossy" size={96} />;
}The root export is framework-agnostic and dependency-free — use it in Node, edge runtimes, Vue, or plain HTML.
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 });| option | values | default |
|---|---|---|
| variant | soft · punchy · glossy | glossy |
| radius | none · sm · md · lg · xl · 2xl · 3xl · squircle · full | full |
| size | any pixel value | 512 |
This site also serves avatars over HTTP — same engine, same options as query params. Handy for READMEs and places you can't run JavaScript.
<img
src="/api/avatar/maya?variant=glossy&radius=full&size=256"
width="64"
height="64"
alt="maya"
/>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.
PixelAvatar takes radius and size — there's no variant, since the palette is fixed.
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.
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 });| option | values | default |
|---|---|---|
| radius | none · sm · md · lg · xl · 2xl · 3xl · squircle · full | full |
| size | any pixel value | 512 |
And over HTTP, from its own endpoint:
<img
src="/api/pixel/maya?radius=full&size=256"
width="64"
height="64"
alt="maya"
/>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.
It lives on its own /loaders entry so the animation code stays out of bundles that only need avatars. Every prop is optional.
import { Spindle } from "facedye/loaders";
export function Saving() {
return <Spindle pattern="snake" animationStyle="flip" />;
}| option | values | default |
|---|---|---|
| pattern | diagonal · snake · ripple · diamond · radar · spiral · orbit · … | diagonal |
| animationStyle | pulse · breathe · pop · blink · glow · flip · squash · spin | pulse |
| size / rows / cols | grid dimensions | 4 |
| speed | seconds per cycle | 0.75 |
See all patterns and animation styles running live on the loaders page.
The deterministic internals are exported too, if you want to build a custom renderer on the same palette.
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);