AI mascot

Pro

A small assistant character, a soft pebble with pill eyes and a companion dot, that shows what the AI is doing. It breathes and glances when idle, leans in to listen, looks up while the dot orbits its head, reads along the line it writes, squints with a sparkle when done and shakes once with worried brows on an error.

Live · keyboard ready
Atlas, ready
Atlas is ready when you are
  • Give an assistant a face that shows what it is doing: in a reply header, beside a composer, on an image canvas while it generates, or in an empty state.
  • Products that want generation to feel alive and friendly without a spinner, with states that read at 24px.
  • Use agent-status when people need the steps, tools and elapsed time of a run, not just a mood.
  • Use text-shimmer alone for a plain "Thinking" line in dense interfaces where a character would distract.
  • Do not use it as a progress indicator with a known percentage; use a progress bar.

Installation

Pro source and install commands unlock with a Pro plan.

example.tsx
import { AiMascot, type MascotStatus } from "@/components/arc/ai-mascot/ai-mascot"; // Inline next to a streaming replyexport function ReplyHeader({ phase }: { phase: "thinking" | "streaming" | "complete" | "failed" }) {  const status: MascotStatus = phase === "streaming" ? "writing" : phase === "complete" ? "done" : phase === "failed" ? "error" : "thinking";  return (    <div style={{ display: "flex", alignItems: "center", gap: 8 }}>      <AiMascot status={status} size={24} name="Atlas" followPointer={false} />      <span>Atlas</span>    </div>  );} // Empty state: large, watching the pointer anywhere on the page<AiMascot status="idle" size={160} followPointer="page" name="Atlas" /> // In a voice composer while the microphone is open<AiMascot status={recording ? "listening" : "idle"} size={40} tone="ink" announce /> // Brand color body with light eyes<AiMascot status="thinking" size={96} color="#7747ff" eyeColor="white" />

AiMascot

A small assistant character drawn in SVG: a soft pebble with pill eyes, a quiet form shade and a companion dot. Six states, each a continuous motion with no loop seam. Idle breathes, floats, blinks and glances around. Listening leans in with wider eyes while the dot pulses like a voice level. Thinking tilts the head and looks up as the dot orbits it, passing behind and in front. Writing looks down and reads along a line the dot writes, then the pen lifts and returns. Done squints happily as the dot turns into a sparkle. Error shakes once and shows worried brows. Status changes blend the two poses over 420ms, never a hard cut, and the eyes follow the pointer on hover.

PropTypeDefaultDescription
status"idle" | "listening" | "thinking" | "writing" | "done" | "error"–What the assistant is doing. Leave unset to play through every state on a loop (the docs demo).
sizenumber96Width and height in px, from 24 (inline next to a message) to 160 (empty state). Below 56 a compact drawing takes over: tighter framing, bolder eyes and dot, no ground line.
tone"accent" | "ink" | "soft""accent"Body color from the theme: the accent with accent-foreground eyes, the foreground ink with background eyes, or a muted surface with a 1px border and foreground eyes.
colorstring–Any CSS color for the body. Overrides tone; the eyes become a dark mix of it unless eyeColor is set.
eyeColorstring–Any CSS color for the eyes and brows.
followPointerboolean | "page"trueEyes follow the pointer while it is over the mascot (true), anywhere on the page ("page", good for empty states) or never. Touch is ignored. Thinking and writing keep their own gaze.
companionbooleantrueShow the companion dot that floats, pulses, orbits, writes and becomes the done sparkle.
groundbooleantrueThe soft contact shadow and the writing line under the mascot. Drawn only at 56px and up.
pausedbooleanfalseFreeze on the current frame. The mascot also pauses by itself off screen and in background tabs, and resumes without a jump.
namestring"Assistant"Used in the accessible label, for example "Atlas, thinking".
labelstring–Overrides the whole accessible label.
announcebooleanfalseAnnounce status changes in a polite live region. Turn it on for the one mascot that carries status, not for decorative copies.
onStatusChange(status: MascotStatus) => void–Called when the self-playing loop (status unset) moves to the next state. Use it to sync a caption or a control.
classNamestring–Class on the root span. Set --mascot-body, --mascot-eye, --mascot-dot, --mascot-shade or --mascot-ground here to restyle.
styleCSSProperties–Inline style on the root span.
None
The mascot is a status image and takes no focus. Pair it with a real control when people act on what it shows.
  • The SVG has role img and a label built from name and status, for example "Atlas, thinking", which updates with every state.
  • With announce, status changes are read in a polite live region outside the image. Use it on one mascot per view so screen readers do not hear duplicates.
  • Status is never carried by motion alone: each state changes the face (wide eyes, eyes up, eyes down, happy squint, worried brows) and the label. Show a text status next to it where the state matters.
  • Pointer following ignores touch and is off under reduced motion.
  • Every state is a continuous function of time (sums of slow sines, an orbit angle, a writing cycle whose start and end meet at zero length), so loops never restart and have no seam.
  • A status change blends the old pose into the new one over 420ms with an ease in and out; a change during a blend starts from the frame on screen. The first paint (and the server render) is the still pose, and the loop eases out of it over the same 420ms, so nothing jumps on mount. Eyes ride a critically damped spring: quick saccades when idle, a soft follow while thinking, tight tracking while writing. Nothing bounces.
  • Done passes the eyes through a closed line into the happy squint while the dot shrinks and the sparkle grows in its place. Error shakes once, decaying to rest in about .7s.
  • The dot is drawn twice, behind and in front of the body, and switches layers only at the ends of its orbit where it is clear of the body. While it crosses the crown in front it fades into the eye color, so it stays visible on the body.
  • Blinks come every 2.4 to 5.2s, sometimes twice, and take 160ms.
  • Reduced motion shows a still, representative pose for each state (eyes up with the dot over the head, half a written line, the squint and sparkle, the brows), switches in place and drops every loop, glance, blink and shake.
  • A fixed square of size px that never reflows its parent. The drawing scales crisply as vector art at any device pixel ratio.
  • Below 56px the compact drawing frames the body tighter and enlarges the eyes, dot and sparkle so they stay legible at 24px.
  • One requestAnimationFrame loop per mascot writes SVG attributes directly through refs, so React does not re-render per frame; only the self-playing loop sets state, once per state.
  • The loop stops off screen (IntersectionObserver) and in hidden tabs, and its clock only runs while visible, so it resumes on the same frame instead of jumping.
  • Under reduced motion the loop repaints only when the status changes.
  • About fifteen small SVG nodes, with no filters, masks or gradients.

Notes for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Map your assistant's real phases to status: microphone open is listening, waiting for the first token is thinking, streaming is writing, finished is done, failed is error, otherwise idle.
  • Keep thinking short in real use; switch to writing on the first token so the mascot reads what it writes.
  • Use size 24 to 32 inline in a message header or composer, 40 to 64 in a side panel header, 96 to 160 in an empty state. Below 56 the drawing simplifies on its own.
  • Set name to the assistant's name so the label reads well. Turn announce on for one mascot only.
  • Leave status unset only for showcases; in a product always pass it.
/components/ai-mascot/markdown

The full library index for assistants is at /llms.txt.