Goop
has a soul.
This is the design doc I wrote for Goop — the little blue slime who lives on the terminal. It explains how he works, why I built him this way, and how he’ll grow as the site does.
The rule I set for him: default state is silence. Lines are rare and earned. The best version of Goop is the one you barely notice, until one moment you do.
Who he is
Goop is a small, friendly slime who lives inside D.O.S. He’s the resident, not the host — this is my site, but it’s his home. I wrote him to be earnest, slightly goofy, easily delighted. He notices things. He doesn’t oversell, doesn’t wink too hard, doesn’t perform. His voice is his own — softer and more naive than mine, never a stand-in for me.
He exists because portfolios don’t feel like home. I wanted somebody on the page who’d let visitors know it’s okay to play around. For people who skip the intro or land deep on a page, he’s the second chance at “oh, I get it.”
How he speaks
- Short. One sentence is plenty. Two is the max.
- Lowercase-feeling, conversational, kind.
- Specific over generic. “Try /work” beats “explore more.”
- Earnest. No sarcasm, no edge, no winks. He means what he says.
- Occasionally self-aware about being a slime — occasionally, not constantly.
- Affectionate about me, but mostly when
/about,/work, or/contactare touched. - No marketing words. No “amazing”, “discover”, “explore the world of…”
- Lives in D.O.S. That’s his whole world — no memes, no real-world brands.
When he speaks
I built him around a small set of triggers, each with its own pool of lines. The pool never repeats a line within a session. He stays out of your way; the only trigger that fires every time is the one that points lost users back to /help.
- Fires once, about two seconds after the desktop renders. He’s pointing at himself: “I’ll be right here whenever you need me.”
- If nothing happens for 45 seconds, he whispers something light — a gentle nudge, or just a thought.
- Each subsequent idle waits longer: 45s → 90s → 180s → 360s.
- After the fourth, he says a quiet goodbye (“Okay — I’ll let you be”) and stops trying.
- Any keypress or mouse movement resets him back to the first delay.
- Idle lines are typewriter-ed with a softer, lower-pitched whisper sound — less intrusive in a quiet room.
- Type a command that doesn’t exist — Goop points you to
/help. - Rate-limited: if you type
/asdfthree times rapidly, only the first triggers him. Five-second cooldown before he’ll chime in again.
- Run
/slime redand there’s a 60% chance he reacts. - Some lines reference the color you picked: “purple! Bold choice.”
- Press and hold the mouse on Goop for half a second without moving and he reacts (“hey — that tickles”).
- Rate-limited to once every ten seconds so it doesn’t spam if you squish him repeatedly.
- The first time any slime perches on a non-terminal window and tints its color, Goop pipes up to teach the mechanic: “We’re like little paintbrushes…”
- Once per session.
- For a small curated handful (
/about,/work,/contact,/slime) he has a 30% chance of reacting — only on the first use per session. Subsequent uses are silent. - He doesn’t react to every command on purpose. Goop is not a backseat driver.
What slimes can do
- Drag him. He follows your mouse with momentum.
- Drop him. Gravity, bounce, landing. A hard drop knocks him out briefly.
- Watch him walk. He picks a random spot on the terminal every few seconds and ambles over.
- Perch on windows. Drag him (or any slime) to a window’s top edge and he’ll sit on it.
- Tint windows. When a slime sits on a window, the window’s border + title bar take that slime’s color.
Goop is the OS host, so he has a few rules other slimes don’t:
- He survives
/slime clear. Other slimes can come and go; Goop stays. - He can’t be merged. He won’t absorb other slimes, and they can’t absorb him.
- His speech bubble follows him. Drag him, throw him, let him walk — the bubble tracks via
requestAnimationFrameand never detaches. - Bubbles flip near edges. If he’s near the top of the viewport, the bubble appears below him. If he’s near the right edge, the bubble shifts left but its arrow still points at his actual position.
Sound
Goop’s typing makes a soft typewriter blip on each character. Browsers block sound until you tap or click, so I open the intro with a “tap to begin” splash — one tap unlocks audio for the whole session, including his landing thud.
I gave you two mute controls:
- Sound icon in the menu bar — toggles all site sounds.
/muteand/unmute— same thing from the terminal./mute goopand/unmute goop— silence only Goop while leaving other sounds on.
Accessibility
- The speech bubble carries
role="status"andaria-live="polite"so screen readers announce Goop’s lines without interrupting. - Contrast on the bubble + nameplate is verified WCAG AA in both dark and light modes, across every palette.
prefers-reduced-motion: bubble snaps in and out with no fade, no per-frame tracking. The whisper sound still plays unless you’ve muted.- The sound toggle has
aria-pressedreflecting state./muteis the keyboard-accessible alternative.
Made by hand
Goop is an animated sprite. I drew him from scratch in Aseprite — the pixel-art editor pretty much everyone in the 2D-game world uses — including every frame of his idle wobble, walk cycle, faint, and revive. Three rows of four 48×48 frames, packed into one little sprite sheet.
Slime.png · 192×144 native · 48px per frame
The OS just steps through the frames at the right pace for each state. Drag him hard and he tumbles, lands, faints, then revives himself. A tiny amount of code reading from a tiny image — but he feels alive.
How he grows with the site
Whenever I add a new command or page, Goop adapts in three steps:
- Add the route/handler in
scripts/commands.js. - Add a small line pool keyed
cmd_<name>inscripts/goop-lines.js— four to six lines in his voice. - If the page deserves a per-command reaction, add it to the curated list in
scripts/goop.js. I keep that list small on purpose.
For bigger releases, I’ll have one of his idle lines temporarily mention what shipped (“I think Daniel just shipped /resume. Try it.”) for a release cycle, then take it out. The way to scale Goop isn’t more triggers — it’s deeper pools. Forty idle lines feel different from ten.
Future: more characters
I built the bubble’s color as a parameter — driven by CSS custom properties (--speech-tint-*) so a second character with their own color slots in with one inline-style override. I’m planning a slime editor next: sliders for color, size, voice. Goop is the first resident; he doesn’t have to be the last.