seedicon
v0.1.0
Published
Deterministic SVG avatars generated from any string seed (wallet address, UUID, user id) — no image storage, no uploads.
Maintainers
Readme
seedicon
Deterministic SVG avatars generated from any string — a wallet address, a UUID, a user ID, an email. Same seed in, same avatar out, every time, forever. No image storage, no uploads, no CDN.
npm install seediconWhy
If you let users upload a profile picture, you also have to store it,
resize it, moderate it, and serve it. Most apps don't need any of that —
they need something in the avatar slot that's consistent and looks
intentional. seedicon generates that something on the fly from a seed
you already have, so the only thing you store is a string (or nothing at
all, if you just derive it from the user's id every time you render).
Usage
import { generateAvatar } from "seedicon";
const svg = generateAvatar({ seed: "0xba32...eD56" });
// '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" ...>...</svg>'svg is a plain string — write it to a file, inject it with innerHTML,
or embed it as a data URI:
import { generateAvatarDataUri } from "seedicon";
const src = generateAvatarDataUri({ seed: user.id });
// '<img src="data:image/svg+xml,..." />'Options
generateAvatar({
seed: "anything", // required — the only thing that determines the output
style: "pixels", // see the list below — default "pixels"
size: 64, // SVG width/height in user units — default 64
radius: 0, // corner radius, e.g. 12 for rounded, size/2 for a circle — default 0
});Nine styles, pick whichever fits your product (or let users pick).
Three of them are output-compatible with an existing library — the same seed produces the exact same image, so you can swap the library out without changing anybody's avatar:
| Style | What it looks like | Compatible with |
| --- | --- | --- |
| pixels | Mirrored pixel grid, the wallet-avatar classic | blockies |
| jdenticon | Geometric shapes with four-fold rotational symmetry | Jdenticon |
| stellar | 7×7 mirrored bit grid, the "space invader" look | stellar-identicon-js (for real Stellar addresses) |
The other six are seedicon's own:
| Style | What it looks like |
| --- | --- |
| identicon | Five symmetric rows of pixels — the classic dev-tool identicon |
| ring | Concentric rings in seed-derived colors and thicknesses |
| lifehash | An organic pattern grown with a Game of Life automaton |
| marble | Two soft, blurred organic blobs over a flat color |
| waves | Layered wave bands in shades of a single hue |
| gradient | A soft two/three-color diagonal gradient |
Importing a single style
The package root resolves styles by name, so it has to reference all nine and no bundler can drop the ones you never call. If you only use one style, import it directly and you get that renderer plus the shared core — nothing else:
import { ring, ringDataUri } from "seedicon/ring";
const svg = ring({ seed: user.id, size: 40, radius: 20 });Every style has an entry point named after it: seedicon/pixels,
seedicon/identicon, seedicon/jdenticon, seedicon/stellar,
seedicon/ring, seedicon/lifehash, seedicon/marble,
seedicon/waves, seedicon/gradient. The <Avatar> component takes
style names, so it pulls in all nine — if bundle size matters more than
the convenience, call the single-style function and render the markup
yourself.
React
import { Avatar } from "seedicon/react";
<Avatar seed={user.walletAddress} style="pixels" size={40} />;Unlike canvas-based generators, this renders synchronously to SVG — no
useEffect + ref dance, no flash of an empty avatar, and it works during
server-side rendering (Next.js, Remix, etc.) because there's no canvas
or window involved. react is a peer dependency and only needed if you
import from seedicon/react.
Database
Store the seed you already have (a user id, a wallet address) and call
generateAvatar at render time — nothing to persist. If you'd rather
store a ready-to-use value (e.g. because you want to let the style be
changed independently of re-deriving it), a plain string column works
fine too:
// on signup
user.image = "pixels"; // or generateAvatarDataUri({ seed: user.id, style: "pixels" })
// on render
<Avatar seed={user.id} style={user.image /* any style name */} />Migrating from blockies
The pixels style is a port of the blockies algorithm, verified
against @download/blockies cell-for-cell and color-for-color. Swapping
the libraries does not change a single avatar:
- import { createIcon } from "@download/blockies";
- const canvas = createIcon({ seed: address, size: 8, scale: 8 });
- ref.current.appendChild(canvas);
+ import { Avatar } from "seedicon/react";
+ <Avatar seed={address} size={64} />What you gain is that it renders during SSR: no <canvas>, no DOM ref,
no useEffect, no flash of empty avatar on first paint.
One thing to watch: casing. An Ethereum address exists in two forms —
lowercase (0xba32…ed56) and checksummed (0xBa32…eD56) — and they are
different strings, so they produce different avatars. blockies behaves
the same way; it does not normalize either. Whatever your app passes
today, keep passing exactly that, or normalize once at the edge:
<Avatar seed={address.toLowerCase()} />seedicon deliberately does not lowercase for you: usernames and emails
are seeds too, and Maria should be free to differ from maria.
Determinism
seedicon has no randomness and no I/O. The same { seed, style, size,
radius } always produces byte-for-byte identical SVG markup, on any
platform. It's not a cryptographic hash — don't use it for anything
security-sensitive — it only needs to spread seeds out visually, which a
fast 32-bit hash does well enough.
Credits
Three styles are ports. Their algorithms are not ours, and the test suite compares seedicon's output against the original packages on every run so compatibility cannot quietly drift:
- pixels — blockies by Erin Dachtler and Alex Van de Sande (MIT).
- jdenticon — Jdenticon by Daniel Mester Pirttijärvi (MIT).
- stellar — stellar-identicon-js by Lobstrco (ISC). Real Stellar public keys are decoded exactly as the original does; any other seed falls back to a SHA-1 digest, since an arbitrary string has no key bytes to read.
Each was reimplemented to emit SVG rather than paint to a canvas — that is the part seedicon adds, and it is why these work during SSR.
The remaining styles are original implementations, inspired by:
- identicon and waves — the styles of the same name in DiceBear (created by DiceBear, CC0 1.0).
- lifehash — LifeHash by Blockchain Commons (BSD-2-Clause). Same mechanism — a Life automaton colored by how long each cell survived — at a fraction of the size.
- ring and marble — the themes of the same name in Boring Avatars.
License
MIT
