Botling Stable
Seeded robot avatars: tens of millions of robots from 7 heads, 14 eyes, 11 mouths, 7 tops and more, in 16 palettes and 4 cell shapes. Below 40px Botling draws a simpler 9x9 robot from the same traits, so it keeps a face at 24px. Try it in the playground.
import { botling, botlingDataUri } from 'seedkin/botling';
const svg = botling(user.id, { frame: 'circle' });
const src = botlingDataUri(user.id, { size: 40, title: null });
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. |
look |
'gapped' | 'flat' | 'inked' | 'soft' | 'soft-inked' | 'sticker' |
'gapped' |
How the cells are drawn: apart, or merged into flat shapes. See Looks. |
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.
'square'(the default) fills the image, as every robot did before frames.'rounded'has corners a fifth of its width round. The robot keeps its size; the small robot can lose a sliver of a shoulder at the corners.'squircle'is a smooth app-icon curve. Wide shoulders reach into the corners it cuts, so the robot draws at 92% to fit, the same for every seed so a list of avatars lines up.'circle'crops the robot to a circle at full size. Shoulders run off the edge like a portrait, and so can the tips of the widest antennas and ears.
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.
Looks
botling(user.id, { look: 'soft' });
look sets how the cells are drawn. The same seed draws the same picture in every look; only the drawing changes.
'gapped'(the default) draws each cell apart, over a faint grid, like pixels on a display.'flat'closes the gap, so cells merge into solid shapes. Circles become touching beads, and diamonds and triangles tile into facets.'inked'is flat with a dark outline round the avatar and thin lines between colours, like a cel-shaded cartoon.'soft'rounds the outer corners of each patch of colour and curves its inner ones, so the avatar reads as vector shapes.'soft-inked'is soft with the dark outline.'sticker'is soft with a white die-cut border and a drop shadow. The avatar draws at 86% to make room for them.
Only 'gapped' has a grid, so showGrid does nothing in the other looks. Rounded corners and the lines between colours need square cells: with circles, triangles or diamonds, 'soft' draws like 'flat', and 'inked' and 'sticker' keep their outline and border. Small avatars, below 40px, already draw their cells touching, so there 'flat' changes nothing and the other looks add their outline, corners or border. The ink is the palette's shade colour darkened. LOOKS lists the six, for a picker. A seed never picks a look, so store the one a user chooses with their seed. Every style takes look; animated robots keep their outline on their moving parts.
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:
- Hover. The whole robot bobs up and down over its background.
- Blink. Every 2 to 4.5 seconds, and about a third of robots blink twice. A lid in the face colour closes over the eye, so robots without a screen blink too. A wink blinks its open eye, an angry brow stays put, and sleepy eyes, already shut, don't blink.
- Wobble. The antenna or other top piece sways from its base and settles; twin antennas and horns sway as a mirrored pair.
- Twitch. Ears, handles and bolts hop up twice every 2 to 3 seconds, the right a beat after the left.
- Talk. The mouth squashes like a short sentence, then rests.
- Scanline. A glowing band sweeps down the screen every 3 to 5 seconds.
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.
- Reduced motion. It all sits inside
@media (prefers-reduced-motion: no-preference), so the robot holds still for anyone who has asked for less motion, and that still robot is the one drawn withoutanimate. Motion that runs on its own for more than five seconds should also be stoppable from the page (WCAG 2.2.2), so offer a still option, as the playground does. - A strict CSP. An inlined SVG's
<style>counts as an inline style, so a page withstyle-src 'self'blocks it and shows the still robot. An<img>is its own document, sobotlingDataUri(id, { animate: true })animates under any CSP that allowsdata:images. - Cost. About 2.4 KB more per robot at full size (up to 4.5 KB with a big screen to scan), half a kilobyte below 40px. Each animated robot keeps its image repainting, so animate the few that matter (a profile, a hero, the current speaker) rather than a list of 50.
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, look and frame next to their seed, and pass them back in. LOOKS and FRAMES list the looks and 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.