TheSquad's pixel squid: 31 emotions, 53 costumes, 34 moves, 32 things to hold, 7 rides, 13 backdrops, 4 shapes, the whole team (55 people) and a few easter eggs, for empty states and anywhere else a glyph would be too dry.
<Inky size={192} />A teammate or preset fills in the layers below; anything you set there wins.
Custom skin (hex) is required
npx shadcn@latest add https://sdk-components.thesqd.com/r/inky.jsonimport { Inky } from "@/components/ui/inky";import { Inky } from "@/components/ui/inky";
export function NothingToDo() {
return (
<div className="flex flex-col items-center gap-2">
<Inky />
<p>Nothing is waiting on us.</p>
</div>
);
}Inky (one canvas; pass everything as props)
├── size={96} (frame side in px; keep it a multiple of 32)
├── emotion="happy | joy | sad | …" (pin a face; 30 in INKY_EMOTIONS)
├── costume="santa | crown | …" (something to wear; 52 in INKY_COSTUMES)
├── action="idle | dance | …" (a looping move; one-shots go through ref.play)
├── skin="ogre | #5fb34a" (his colour: a name from INKY_SKINS or any hex)
├── shape="ghost | jelly | slime" (changing it plays the morph)
├── holding="laptop | mic | …" (held out at his side; INKY_HOLDINGS)
├── vehicle="bus | rocket | …" (something to ride; INKY_VEHICLES)
├── backdrop="sea | space | …" (what is behind him; INKY_BACKDROPS)
├── stageWidth={480} | "fill" (a wider stage; "fill" fills his card)
├── thought="…" (a pixel-art thought bubble over his head)
├── say="…" (a speech bubble: words he says)
├── sign="…" (a placard he holds up in one tentacle)
├── person="josh" (a teammate's hair, glasses and beard; INKY_PEOPLE)
├── preset="artist | disco | …" (a named bundle of the props above)
├── occasion={{ kind: "birthday", name }} (cake, party hat, confetti, a bubble saying so)
├── weather="rain | auto" (a backdrop for the weather; auto never prompts)
├── progress={0.6} (how far along; the garden grows with it)
├── particles / random (quiet him; let him pick his own faces)
├── scene (draw the sea behind him; same as backdrop="sea")
├── paused (freeze on the current frame)
├── interactive (hover/click reactions; on unless emotion is pinned)
├── eggs (the easter eggs; on wherever he is interactive)
└── ref → { play, flash, party, morph, sequence, stop } (all but stop return promises)
lib/inky-engine.ts (the painter; its header maps the layers below)
├── inky-skin · inky-body · inky-faces · inky-wear · inky-people
├── inky-hold · inky-ride · inky-motion · inky-effects · inky-stage
├── inky-thought · inky-sign · inky-presets · inky-brain · inky-moments · inky-pixels
├── seasonalCostume(date) (what he wears on his own that day)
└── pickEmotion(text) (a face for a line of copy; inkyEmotionGuide() for an LLM)| Prop | Type | Default | Description |
|---|---|---|---|
size | number | 96 | Side of the square frame, in px. The squid is half the frame wide; the rest is room for hats, hearts and confetti. Keep it a multiple of 32. |
emotion | InkyEmotion | — | Pin a face. Unset, he rests on `happy` and reacts to hover. Every name is in `INKY_EMOTIONS`. |
costume | InkyCostume | — | Something to wear. Unset (and unpinned, with eggs on), he dresses for the season. Every name is in `INKY_COSTUMES`. |
action | InkyAction | "float" | The move he holds. Unset, he floats (`INKY_REST`); `idle` is the small bob on the spot. One-shots (`INKY_ONE_SHOTS`) play once and settle back to floating; play them again with `ref.play()`. |
skin | InkySkin | string | — | His colour: a name from `INKY_SKINS` or any `#rrggbb`. The shade is derived. |
shape | InkyShape | "squid" | Shift into `ghost`, `jelly` or `slime`. Changing it after he appears plays the morph. |
holding | InkyHolding | — | Something held out at his side. Every name is in `INKY_HOLDINGS`. |
vehicle | InkyVehicle | — | Something to ride; it stays grounded and drives off on travelling moves. Every name is in `INKY_VEHICLES`. |
backdrop | InkyBackdrop | — | What is behind him: `sea`, `disco`, `space`, `beach`, `snow`, `rain`, `spotlight`, `office`, `church`, `night`, `garden`, `launch`, `map`. |
stageWidth | number | "fill" | — | A stage wider than his frame, in px, or `"fill"` for the width of what he sits in (it follows resizes). Walkaway, zoomies and moonwalk cross it. |
thought | string | — | A pixel-art thought bubble over his head; it follows him and flips side when there is no room. |
say | string | — | A speech bubble over him, with a pointed tail instead of thought dots; wins over `thought`. |
sign | string | — | A placard he holds up in one raised tentacle, with pixel text on it; it follows him as he moves and takes the place of `holding`. A change lowers the sign and raises it with the new words. |
person | InkyPerson | — | A teammate: their hair, glasses and beard, what they hold and their smile (`INKY_PEOPLE`). Props you set win. |
preset | InkyPreset | — | A named look bundling the props above (departments, occasions). Props you set win. |
occasion | { kind: "birthday" | "anniversary"; name?: string; years?: number } | — | Dress him for the day: cake or popper, party hat, the launch stage and a bubble ("Happy birthday, Tia!", "5 years, Derek!"). Props you set win. |
weather | InkyWeather | "auto" | — | A backdrop for the weather (`clear`, `cloudy`, `rain`, `snow`, `night`). `"auto"` uses the viewer's local weather only if they already granted location (never prompts), else the time of day. |
progress | number | — | How far along something is, 0 to 1. The garden backdrop grows its flowers with it. |
particles | boolean | true | Hearts, Zs, sparks and bubbles. Off keeps him quiet; overlays like a "?" still show. |
random | boolean | false | Every few seconds he flashes a cheerful face, and half the time plays a move. |
scene | boolean | false | Draw the sea floor, bubbles and his shadow behind him. The same as `backdrop="sea"`. |
paused | boolean | false | Freeze on the current frame. He also stops on his own when scrolled out of view or unmounted. |
interactive | boolean | emotion === undefined | Hover for the next emotion in turn, click to poke (a random other emotion and a jump). |
eggs | boolean | interactive | The easter eggs: Konami code, typing “inky”, poke frenzy, hold for shy, a nap after a minute alone, seasonal outfits and the one-in-fifty duck. |
ref | React.Ref<InkyHandle> | — | Drive him from outside: see the handle below. |
label | string | "Inky, TheSquad's squid" | The canvas's accessible name. |
className | string | — | Extra classes on his wrapper. |
| Prop | Type | Default | Description |
|---|---|---|---|
play(action) | (action: InkyAction) => Promise<boolean> | — | Run a move from its first frame. Resolves true once it has played through (a loop: one cycle), false if another move cut in first. |
flash(emotion, ticks?) | (emotion: InkyEmotion, ticks?: number) => Promise<boolean> | 24 | Show a face for a while (8 ticks a second), then go back to the one he was on. Resolves true when it runs out, false if replaced. |
party() | () => Promise<boolean> | — | The Konami party on this one alone: party hat, dance, disco colours, then back to his move. Resolves when it is over. |
morph(shape) | (shape: InkyShape) => Promise<boolean> | — | Shift into another shape: a squash, a puff of ink, a spring back. Resolves true when done. |
sequence(steps) | (steps: InkyStep[]) => Promise<boolean> | — | Run steps in order, each awaited: `{ play }`, `{ flash, ticks? }`, `{ wait: ms }`, `{ party: true }`, `{ morph }`, `{ call }`. Resolves true when every step ran; false if `stop()`, another sequence, a move or face that cut in, or unmounting ended it. |
stop() | () => void | — | End the running sequence where it is. |