Inky

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.

TSusage.tsx
<Inky size={192} />

Build him

A teammate or preset fills in the layers below; anything you set there wins.

Setup

Installation
$Terminal
npx shadcn@latest add https://sdk-components.thesqd.com/r/inky.json
Usage
Drop him in with no props and he floats, reacts to hover and click, and dresses for the season. Pin `emotion`, `costume` or `action` to hold him still in one state, and dress him with `skin`, `holding`, `vehicle`, `backdrop`, `person` or a `preset`. The engine (`lib/inky-engine.ts` and its `lib/inky-*.ts` layers) installs with him. The builder above is the `inky-builder` block, if you want it in your own app.
TSImport
import { Inky } from "@/components/ui/inky";
TSExample
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>
  );
}

Start

In an empty state
What he is for: the media of an `Empty` when there is simply nothing to do. Hover for the next emotion in turn, click to poke.
Nothing is waiting on us.
Every request on this project has been answered.
Sizes
The frame is 32 sprite pixels square; keep `size` a multiple of 32 so every pixel is whole. 96 is the default.
32px
64px
96px
128px
192px

Look

Emotions
Every face, pinned with `emotion`. Faces with open eyes blink.
happy
joy
sad
angry
surprised
sleepy
love
dizzy
cool
wink
shy
silly
starry
giggle
nervous
yummy
thinking
dealwithit
fine
bruh
sus
uwu
lol
ded
mindblown
celebrate
fireworks
lost
stressed
confused
kiss
Skins
`skin` recolours him: a name from `INKY_SKINS`, or any `#rrggbb` (a brand colour). His shade is derived from it.
classic
brand
indigo
coral
purple
amber
magenta
lavender
lilac
pink
cream
blush
peach
ogre
sky
mint
tangerine
midnight
#00b3a4
Shapes
`shape` shifts him into something else. Change it (or call `ref.morph()`, or a `{ morph }` step) and he goes in a puff of ink and comes back as the new shape. Faces, costumes and moves all still work.
squid
ghost
jelly
slime
Costumes
Every outfit, set with `costume`. They are part of his sprite, so they squash and jump with him.
party
crown
bow
tophat
beanie
santa
headphones
graduate
halo
glasses
bowtie
mustache
clown
pirate
viking
propeller
chef
cowboy
bunny
cat
unicorn
cone
duck
sprout
nerd
tinfoil
cap
tinyhat
googly
fish
bread
wizard
pumpkin
beret
patriot
pilgrim
leprechaun
plumber
elf
ninja
saber
robot
superhero
ogre
fedora
detective
vampire
vigilante
dino
pompadour
workout
madscientist
boywizard
Holidays
Left unpinned (no `costume`, easter eggs on), he dresses for the day himself. `seasonalCostume(date)` says what he would wear.
New Year's Day
Mar 14 to 17
Easter week
April Fools'
Jul 1 to 4
Oct 24 to 31
Thanksgiving week
Dec 1 to 25
1 in 50, any day
People
The whole team: `person="josh"` gives him their hair, glasses and beard, what they hold and their smile. Each look is a line of traits in `lib/inky-people-data.ts`.
Ailen
Alexa
Amber
Andi
Andrea
Ariel
Ashley
Bennett
Blake
Brianna
Christian
Cina
Daniyal
David
Delaney
Derek
Duane
Dustin
Edwin
Euri
Fram
Isaac
Ivan
Ivy
Jacob
Jamicah
Japheth
Jarrid
Jay B.
Jay D.
Jeremiah
Jose
Josh
July
Justine
Karina
Khei
Lynsey
Mackenzie
Mallory
Marcos
Matthew
J McNeel
Meagan
Michael
Michelle
Mykee
Nathan
Roman
Sela
Skyler
Spencer
Susannah
Tia
Tomás
Holding
`holding` puts something in his tentacles. It stays upright while he moves.
phone
laptop
coffee
paintbrush
camera
pencil
clipboard
clapper
mic
megaphone
controller
wrench
magnifier
book
trophy
plant
pizza
donut
balloon
bible
togo
roller
spraycan
reel
map
flag
umbrella
icecream
taco
cake
popper
wand

Scene

Stage
`backdrop` draws the sea or the disco behind him. `stageWidth` widens his stage (px, or "fill" for the width of his card) so a walkaway crosses it.
sea
disco
space
beach
snow
rain
spotlight
office
church
night
garden
launch
map
preset="rerouting", size={160}, stageWidth="fill"
stageWidth="fill", walking away
Rides
`vehicle` sits him in one. It stays on the ground while he bobs, and drives off with him on a travelling move.
car
bus
skateboard
bike
rocket
ufo
boat

Motion

Moves
Set `action` for a loop; one-shots (jump, loop, walk away…) play once through `ref.play()` and settle back.
idle
swim
jump (once)
wiggle (once)
dance
ink (once)
wave
loop (once)
spin
moonwalk
floss
headbang
boogie
zoomies
walkaway (once)
cross
juggle
balance
jumprope
hula
yoyo
horsedance
zombie
dab (once)
dodge (once)
robotdance
disco
sway
float
typing
sleep
highfive (once)
peek (once)
paddle
Sequences
`ref.sequence([...])` runs steps in order, each waited out: a move, a face, a pause, your own callback. `play`, `flash` and `party` also return promises on their own. Hover him mid-routine and it stops, since your hand cut in.
Ready.

Words and moments

Thought bubble
`thought` puts pixel text in a bubble over his head. It follows him, and moves to his other side when there is no room.
Speech bubble
`say` is the thought bubble with a pointed tail instead of dots: words he says, not thinks. It follows him and flips side the same way.
say
say, wordy
Sign
`sign` has him hold up a placard with pixel text on it, its stick in one raised tentacle. It follows him as he moves.
Changing the words
Change `sign`, `say` or `thought` and he swaps the words in view: he lowers the sign behind him and raises it again with the new ones, and a bubble shrinks into its tail and grows back out with them. From code, `ref.sign()`, `ref.say()` and `ref.think()` do the same and resolve once the words are up; `{ sign }`, `{ say }` and `{ think }` are sequence steps. Type below and send it to any of them.
sign
say
think
Ready.
Picking a face
`pickEmotion(text)` chooses a face for a line of copy. `inkyEmotionGuide()` gives an LLM the same guide as prompt text.
“Upload failed, please try again” → sad
“Project approved!” → celebrate
“No results found” → lost
“Generating your preview” → thinking
“Thanks for the feedback” → love
“Deadline is tomorrow” → nervous
Moments
`occasion` dresses him for a birthday or work anniversary and says so. `weather` picks a backdrop for the weather ("auto" reads the viewer's, never prompting). `progress` grows the garden.
birthday
anniversary
weather="auto"
progress={0.6}

Extras

Presets
`preset` is a named look, a bundle of the props above. Any prop you set as well wins.
artist
video
developer
social
support
disco
schoolbus
roadtrip
astronaut
alien
sailor
skater
showtime
beachday
snowday
rainyday
rerouting
Easter eggs
On wherever he is interactive (turn off with `eggs={false}`). The buttons show each one; the real triggers are listed beside them.
Disco partyThe Konami code, anywhere: ↑ ↑ ↓ ↓ ← → ← → B A.
Say his nameType “inky” anywhere on the page.
Poke frenzyClick him five times in two seconds.
ShyHold the pointer down on him.
NapLeave him alone for a minute; hover to wake him.

Reference

Composition
Anatomy of Inky.
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)
API Reference
Props on Inky, and the handle its ref exposes.

Inky

PropTypeDefaultDescription
sizenumber96Side 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.
emotionInkyEmotion—Pin a face. Unset, he rests on `happy` and reacts to hover. Every name is in `INKY_EMOTIONS`.
costumeInkyCostume—Something to wear. Unset (and unpinned, with eggs on), he dresses for the season. Every name is in `INKY_COSTUMES`.
actionInkyAction"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()`.
skinInkySkin | string—His colour: a name from `INKY_SKINS` or any `#rrggbb`. The shade is derived.
shapeInkyShape"squid"Shift into `ghost`, `jelly` or `slime`. Changing it after he appears plays the morph.
holdingInkyHolding—Something held out at his side. Every name is in `INKY_HOLDINGS`.
vehicleInkyVehicle—Something to ride; it stays grounded and drives off on travelling moves. Every name is in `INKY_VEHICLES`.
backdropInkyBackdrop—What is behind him: `sea`, `disco`, `space`, `beach`, `snow`, `rain`, `spotlight`, `office`, `church`, `night`, `garden`, `launch`, `map`.
stageWidthnumber | "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.
thoughtstring—A pixel-art thought bubble over his head; it follows him and flips side when there is no room.
saystring—A speech bubble over him, with a pointed tail instead of thought dots; wins over `thought`.
signstring—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.
personInkyPerson—A teammate: their hair, glasses and beard, what they hold and their smile (`INKY_PEOPLE`). Props you set win.
presetInkyPreset—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.
weatherInkyWeather | "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.
progressnumber—How far along something is, 0 to 1. The garden backdrop grows its flowers with it.
particlesbooleantrueHearts, Zs, sparks and bubbles. Off keeps him quiet; overlays like a "?" still show.
randombooleanfalseEvery few seconds he flashes a cheerful face, and half the time plays a move.
scenebooleanfalseDraw the sea floor, bubbles and his shadow behind him. The same as `backdrop="sea"`.
pausedbooleanfalseFreeze on the current frame. He also stops on his own when scrolled out of view or unmounted.
interactivebooleanemotion === undefinedHover for the next emotion in turn, click to poke (a random other emotion and a jump).
eggsbooleaninteractiveThe 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.
refReact.Ref<InkyHandle>—Drive him from outside: see the handle below.
labelstring"Inky, TheSquad's squid"The canvas's accessible name.
classNamestring—Extra classes on his wrapper.

InkyHandle (ref)

PropTypeDefaultDescription
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>24Show 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.