seedkin

Docs

Every Seedkin style has its own entry point, so an app only ships the styles it uses. Botling, the robots, is the first: seedkin/botling. Snoot (animals), Noggin (people) and Beastie (monsters) are in Preview at seedkin/snoot, seedkin/noggin and seedkin/beastie.

Coming from the botling package? Import from 'seedkin/botling' instead of 'botling': every seed draws the same robot as botling 1.2.0.

Install

npm install seedkin

Use

import { botling, botlingDataUri } from 'seedkin/botling';

// An SVG string, for inlining.
const svg = botling(user.id);

// A data: URI, for <img src> or CSS url().
const src = botlingDataUri(user.id, { size: 48 });

React:

<img src={botlingDataUri(user.id, { size: 40, title: null })} alt="" width={40} height={40} />

Use an opaque, stable id as the seed (a database id or UUID), not an email address or username. The seed never appears in the SVG, but anyone can render the robot for a guessed email and compare.

The seed is used as given, apart from Unicode normalisation (NFC), so José draws the same robot however the é was typed. It isn't trimmed or lowercased: Ada and ada are different robots, so tidy the seed first if your ids vary like that.

Random robots

randomSeed() returns a readable random seed, like plucky-servo-427, for a "give me a new robot" button or a default for someone without an id. Store the seed: that's what brings the same robot back.

import { botling, randomSeed } from 'seedkin/botling';

const seed = randomSeed(); // save this with the user
const svg = botling(seed);

There are 400,000 of these seeds, so two people can get the same one. To give every user their own robot, seed with their id. It uses crypto.getRandomValues where there is one (browsers, Node, edge runtimes) and Math.random otherwise, or pass your own source: randomSeed(() => myRng()).

Options

Option Type Default What it does
shape 'square' | 'circle' | 'triangle' | 'diamond' picked by the seed Cell shape.
palette number | Palette picked by the seed Index into PALETTES (0-15), or a palette object: one of HOLIDAY_PALETTES, or your own with hex colours.
size number 96 Width and height in pixels. The SVG scales cleanly to any size.
detail 'auto' | 'full' | 'small' 'auto' 'full' is the detailed 15x15 robot. 'small' is a simpler 9x9 robot from the same traits, with touching cells, made to read at 24px. 'auto' draws 'small' below 40px.
showGrid boolean | 'auto' 'auto' Draw the empty grid cells. 'auto' draws them from 40px up; below that they read as noise. The small robot never draws them.
frame 'square' | 'rounded' | 'squircle' | 'circle' 'square' The outline of the background. See Frames.
background boolean true Draw the palette's background.
title string | null 'Robot avatar' Accessible name. null marks the image decorative (aria-hidden).
animate boolean false The robot hovers, blinks and talks, its top piece wobbles, its ears twitch and a scanline sweeps its screen. Below 40px it only blinks and hovers. See Animation.

Bad options (an unknown shape, an out-of-range palette, a palette colour that isn't hex, a size of 0) throw a RangeError rather than drawing nothing.

Frames

botling(user.id, { frame: 'circle' });

frame sets the outline of the background. Outside it the image is transparent, so a round avatar sits on any page colour without CSS.

The grid behind the robot keeps only whole cells inside the frame, so its edge steps round the curve, and in a squircle it fills the room the smaller robot leaves. FRAMES lists the four, for a picker. A seed never picks a frame, so store the one a user chooses with their seed.

Every frame but 'square' clips with a <clipPath>. Its id names the frame and its size (botling-circle-500-16000), so robots inlined on one page share identical clip paths instead of clashing. In an <img> or a data: URI each robot is its own document, so ids never meet.

Animation

botling(user.id, { animate: true });

animate: true adds a small CSS animation inside the SVG, so it moves wherever an SVG image does: inlined, in an <img>, as a CSS url() or a data: URI, with no script. Every robot moves:

Below 40px the robot only blinks and hovers, since anything busier reads as flicker at 24px. Timing comes from the seed, so a page of robots doesn't move in unison.

Holiday palettes

HOLIDAY_PALETTES has New Year's, Valentine's, St Patrick's, Fourth of July, Halloween and Christmas. A seed never picks one, so everyday robots don't change; pass one in to dress the same robot up for the day:

import { botling, HOLIDAY_PALETTES } from 'seedkin/botling';

botling(user.id, { palette: HOLIDAY_PALETTES.halloween });

Building a picker

Everything that goes into a robot is exported, so you can offer users a choice:

import { botling, PALETTES, SHAPES, traits } from 'seedkin/botling';

// Let someone choose a shape and palette for their own seed.
const options = SHAPES.flatMap((shape) => PALETTES.map((_, palette) => botling(user.id, { shape, palette, size: 48 })));

// Or show what a seed is made of.
traits(user.id); // { head: 'tv', eyes: 'visor', mouth: 'grille', top: 'antenna', ... }

Store the user's chosen shape, palette and frame next to their seed, and pass them back in. FRAMES lists the frames, as SHAPES lists the cells.

Advanced

These are exported for drawing a robot some other way, such as onto a canvas or into a PNG (the playground's link preview does this). They follow the same versioning as the SVG: a seed's traits and grids only change in a major version.

Export What it is
traits(seed) Every choice that makes up the seed's robot (Traits).
resolveTraits(seed, options) traits with the shape and numeric palette options applied, checked the way botling checks them.
grid(traits) The detailed robot as a 15x15 array of Role values, indexed [row][column].
smallGrid(traits) The simpler robot drawn below 40px, as a 9x9 array of Role values.
Role What a cell is part of: Empty (0), Body (1), Screen (2), Glow (3), Accent (4), Shade (5). Each maps to the palette colour of the same name. Without a screen (traits.hasScreen false), paint Glow cells in the screen colour, as botling does.
GRID_SIZE, SMALL_GRID_SIZE, CENTER 15, 9, and the detailed grid's centre column (7).

The grids are the robot before it's drawn: cell shape, padding and animation are left to you.

How it works

The seed is hashed (FNV-1a) into a small seeded generator (mulberry32), which picks a palette, head, eyes, mouth, top, side pieces, a few sizes and, now and then, a head marking, a one-sided quirk or an accessory. Those are drawn as roles (body, shade, screen, glow, accent) onto a 15x15 grid, mirrored down the centre column. Small avatars use a 9x9 grid drawn from the same traits. The renderer then draws each cell in the chosen shape and colours each role from the palette.

Today that's 16 palettes, 7 heads, 14 eyes, 11 mouths, 7 tops, 4 side pieces, 4 shapes, 3 head markings, 4 quirks, 4 accessories and a few size and on/off traits: tens of millions of distinct robots, before you count colour. Some parts are drawn for one cell shape: rounded and capsule heads become circles on circle cells, block eyes become rings, domes step by half a cell on triangles and cross eyes become chevrons.

An accessory takes the place of what it covers: headphones replace the top piece and sides, a bow or cap the top piece. A one-sided ear or antenna does the same, unless an accessory already has that spot.

Versioning

The picture is the API. Within a major version, a seed with the same options always draws the same robot. Store a seed, shape and palette index, and you get the same robot through every 2.x release. The robots in seedkin/botling are the robots of botling 1.2.0, seed for seed (ADR 0009).

Don't compare or cache robots by their SVG string across versions; the markup can change while the picture stays put. ADR 0006 has the full list. Snapshot tests guard it.

Snoot Preview

Seeded animal avatars: cats, foxes, bears, bunnies, mice, dogs, pandas, owls, frogs and pigs. Until Snoot is marked stable, the Snoot a seed draws can change in a minor version, so pin the minor (~2.1.0) if you store seeds and need the same animal back.

import { snoot, snootDataUri } from 'seedkin/snoot';

const svg = snoot(user.id);
const src = snootDataUri(user.id, { size: 40 });

snoot() takes the same options as botling() except animate: shape, palette (an index into Snoot's own eight PALETTES, or a palette object), size, detail, showGrid, frame, background and title (default 'Animal avatar'). Below 40px it draws a simpler 9x9 Snoot from the same traits.

The seed is salted, so snoot(id) and botling(id) are unrelated. traits, resolveTraits, grid, smallGrid, SPECIES, EYES, MARKINGS, EXTRAS and SHAPES are exported as for Botling. Try them in the playground.

Noggin Preview

Seeded people avatars in seven skin tones, with twelve hairstyles (including afros and locs), glasses, beards, hats and headscarves. Until Noggin is marked stable, the Noggin a seed draws can change in a minor version, so pin the minor (~2.2.0) if you store seeds and need the same person back.

import { noggin, nogginDataUri } from 'seedkin/noggin';

const svg = noggin(user.id);
const src = nogginDataUri(user.id, { size: 40, skin: 4 });

noggin() takes the same options as snoot(), plus skin, an index into SKIN_TONES (lightest first), so people can pick their own. palette picks one of the six outfits in PALETTES, or takes a whole palette object; title defaults to 'Person avatar'. A seed's hair colour and outfit always stand out from its skin; pick another skin tone and they move to the next colours that do. Below 40px it draws a simpler 9x9 Noggin from the same traits.

traits, resolveTraits, grid, smallGrid, paletteFor, SKIN_TONES, HAIR_COLOURS, HAIRS, EYES, MOUTHS, FACIAL, EYEWEAR, HEADWEAR and SHAPES are exported as for Botling. Try them in the playground.

Beastie Preview

Seeded monster avatars: blobs, ghosts, boxes, tall and wide bodies with one to three eyes, fangs, horns and wings. Until Beastie is marked stable, the Beastie a seed draws can change in a minor version, so pin the minor (~2.2.0) if you store seeds and need the same monster back.

import { beastie, beastieDataUri } from 'seedkin/beastie';

const svg = beastie(user.id);
const src = beastieDataUri(user.id, { size: 40 });

beastie() takes the same options as snoot(): shape, palette (an index into Beastie's own eight PALETTES, or a palette object), size, detail, showGrid, frame, background and title (default 'Monster avatar'). Below 40px it draws a simpler 9x9 Beastie from the same traits.

The seed is salted, so one id's Botling, Snoot and Beastie are unrelated. traits, resolveTraits, grid, smallGrid, BODIES, EYES, MOUTHS, TOPS, SIDES and SHAPES are exported as for Botling. Try them in the playground.