SDK
@pixabots/core — ID encoding, random generation, and parts catalog
@pixabots/core is a zero-dependency package for working with pixabot IDs client-side. Works in browsers, Node.js, and edge runtimes.
Install
npm install @pixabots/corepnpm add @pixabots/coreEncode / Decode
Convert between part indices and 4-char IDs:
import { encode, decode, resolve, resolveId } from "@pixabots/core";
// Indices → ID
const id = encode({ eyes: 2, heads: 1, body: 5, top: 6 });
// "2156"
// ID → indices
const combo = decode("2156");
// { eyes: 2, heads: 1, body: 5, top: 6 }
// ID → part names (combo)
const parts = resolve(combo);
// { eyes: "glasses", heads: "blob", body: "wings", top: "mohawk" }
// ID → part names (one-step shortcut for decode + resolve)
const direct = resolveId("2156");
// { eyes: "glasses", heads: "blob", body: "wings", top: "mohawk" }Random generation
import { randomId, randomCombo } from "@pixabots/core";
const id = randomId(); // e.g. "7133"
const combo = randomCombo(); // e.g. { eyes: 7, heads: 1, body: 3, top: 3 }Seeded generation
Generate deterministic pixabots from any string. Same input always returns the same pixabot:
import { seededId, seededCombo } from "@pixabots/core";
seededId("user@example.com"); // always the same ID
seededId("agent-123"); // always the same ID
seededId("user@example.com"); // same as the first callThis is useful for generating avatars from usernames, emails, or any unique identifier without storing the mapping.
Seeded PRNG primitives
When you need more than a single seeded pick — e.g. generating a sequence of variants from one id — use the underlying PRNG:
import { createRng, hashString, mulberry32 } from "@pixabots/core";
const rng = createRng("2156"); // hashString("2156") → mulberry32
rng(); // 0.0 — 1.0, deterministic from the seed
rng(); // next value in the same sequence
// Or compose the primitives yourself:
const seed = hashString("2156"); // 32-bit unsigned
const next = mulberry32(seed); // () => numberValidation
import { isValidId } from "@pixabots/core";
isValidId("2156"); // true
isValidId("f76a"); // true
isValidId("zzzz"); // false — out of range
isValidId("abc"); // false — wrong lengthParts catalog
Access the full parts catalog:
import { PARTS, partCount, getPart, getPartIndex, totalCombinations } from "@pixabots/core";
totalCombinations(); // 10752
partCount("eyes"); // 16
partCount("heads"); // 8
partCount("body"); // 7
partCount("top"); // 12
getPart("eyes", 2); // { name: "glasses", path: "eyes/glasses.png" }
getPartIndex("eyes", "glasses"); // 2Animation data
The bounce animation frames are also available:
import { ANIM_FRAMES, FRAME_MS } from "@pixabots/core";
FRAME_MS; // 72 (ms per frame)
ANIM_FRAMES; // 8-frame array of { top, heads, eyes, body } offsetsSub-animations (sprite-sheet parts)
Some parts ship as horizontal sprite sheets (frames × 32 wide) so they can animate inside the 16-tick idle super-loop. The bounce itself (ANIM_FRAMES) stays 8 ticks and repeats twice; the longer super-loop gives blinks room to breathe. Each PartOption declares a kind:
static(default) — always frame 0.blink— 2-frame sheet[open, closed]. 16-tick schedule:[0,1,0,1,0,0,0,0, 0,0,0,0,0,0,0,0](two blinks then eight ticks held open).sequence— N-frame sheet played in order. N should divideLOOP_LENGTH(1 / 2 / 4 / 8 / 16) so the sub-loop fits the super-loop.
Resolve a tick → frame index via resolveFrameIndex:
import { PARTS, LOOP_LENGTH, resolveFrameIndex, BLINK_SCHEDULE } from "@pixabots/core";
const visor = PARTS.eyes.find(p => p.name === "visor");
// { name: "visor", path: "eyes/visor.png", frames: 8, kind: "sequence" }
for (let tick = 0; tick < LOOP_LENGTH; tick++) {
const idx = resolveFrameIndex(visor, tick);
// sx = idx * 32 to source-crop the sprite sheet
}
LOOP_LENGTH; // 16
BLINK_SCHEDULE; // [0,1,0,1,0,0,0,0, 0,0,0,0,0,0,0,0]Types
import type { PixabotCombo, PartCategory, PartOption, PartAnimKind, AnimFrame } from "@pixabots/core";
// PixabotCombo = { eyes: number, heads: number, body: number, top: number }
// PartCategory = "eyes" | "heads" | "body" | "top"
// PartAnimKind = "static" | "blink" | "sequence"
// PartOption = { name: string, path: string, frames?: number, kind?: PartAnimKind }
// AnimFrame = { top: number, heads: number, eyes: number, body: number }