Concepts
Navii makes one promise: same seed in → same avatar out, byte-identical within a release. Everything else flows from that.
Determinism
createAvatar(seed) is a pure function. The PRNG is sfc32 seeded from a cyrb53 hash of the seed string. Part picks happen in a fixed order so future part additions go to the end of the stream — adding new variants in a release never shifts existing seeds' selections.
This means: a backend can render the same avatar in Node that the browser renders in React, and a Cloudflare Worker rasters to PNG — all from the same seed, all byte-identical.
Practical consequences:
- Safe to cache aggressively (1-year
immutableheaders on hosted endpoint). - Safe to render on SSR + client — no hydration mismatch.
- Safe to mirror across regions.
Seeds: the rule
The seed determines the avatar. Same seed → same avatar, always. That's the whole contract. Two consequences:
| Seed input | Recommendation |
|---|---|
user.id / UUID | Best. Stable, globally unique. |
user.email | Good. Stable, unique per user. |
user.name alone | Names collide. Two "Alice"s get the same face. |
${name}-${createdAt} | Fine fallback. Bake at signup, not at render. |
Date.now() at render | Don't. Avatar would change every reload. |
If you only have a display name, compose a stable seed at signup (${name}-${createdAt}) and persist it. Never derive the seed from current time at render time.
Parts taxonomy
Every avatar is the composition of eight discrete parts plus five continuous tweaks. The seed picks each.
- Discrete parts — palette, body, eyes, mouth, antenna, accessory, background, topper. See the parts catalog for every variant value rendered.
- Continuous tweaks — hue rotation (±30°), body scale (0.92×–1.08×), eye gap shift (±2), mouth curvature (0.85×–1.15×), antenna tilt (±8°). These keep neighboring seeds from reading identical.
Discrete combos: 22 × 8 × 10 × 10 × 5 × 7 × 3 × 12 = 22,176,000. Continuous tweaks make the effective output unbounded while staying fully deterministic.
Overrides
By default everything is seed-derived. You can override two things via the HTTP API:
palette— force a specific color family.background— forcenone,solid, orring.
Programmatic callers via @usenavii/core can override anything by mutating the AvatarSpec directly. See @usenavii/core docs.