ThinkingOrb
Dotted thought-orb for AI & agent status.
State
Size
Theme
Speed
1.00×
import { ThinkingOrb } from "@desk/ui/components/thinking-orb";
<ThinkingOrb state="shaping" size={64} speed={1} />Usage
@desk/ui/components/thinking-orb
<Stack className="w-full" gap="xl">
<Stack gap="base">
<Text className="text-muted-foreground" size="xs">
State
</Text>
<Box className="flex flex-wrap gap-2">
{THINKING_ORB_STATES.map((value) => (
<button
className={pillClass(state === value)}
key={value}
onClick={() => {
setState(value);
}}
type="button"
>
{value.charAt(0).toUpperCase() + value.slice(1)}
</button>
))}
</Box>
</Stack>
<Box className="flex flex-wrap gap-8">
<Stack gap="base">
<Text className="text-muted-foreground" size="xs">
Size
</Text>
<Box className="flex gap-2">
{THINKING_ORB_SIZES.map((value) => (
<button
className={pillClass(size === value)}
key={value}
onClick={() => {
setSize(value);
}}
type="button"
>
{value}px
</button>
))}
</Box>
</Stack>
<Stack gap="base">
<Text className="text-muted-foreground" size="xs">
Theme
</Text>
<Box className="flex gap-2">
{THEMES.map((value) => (
<button
className={pillClass(theme === value)}
key={value}
onClick={() => {
setTheme(value);
}}
type="button"
>
{value.charAt(0).toUpperCase() + value.slice(1)}
</button>
))}
</Box>
</Stack>
<Stack gap="base">
<Text className="text-muted-foreground" size="xs">
Speed
</Text>
<Box className="flex items-center gap-3">
<input
aria-label="Orb speed"
className="w-36 accent-foreground"
max={3}
min={0.25}
onChange={(event) => {
setSpeed(Number(event.target.value));
}}
step={0.05}
type="range"
value={speed}
/>
<Text className="tabular-nums" size="sm">
{speed.toFixed(2)}×
</Text>
</Box>
</Stack>
</Box>
<Box className="flex flex-col items-center gap-4 rounded-2xl bg-muted/40 py-16">
<ThinkingOrb
paused={paused}
size={size}
speed={speed}
state={state}
theme={theme}
/>
<Button
aria-label={paused ? "Play animation" : "Pause animation"}
emphasis="ghost"
icon={paused ? Play : Pause}
onClick={() => {
setPaused((value) => !value);
}}
size="icon-base"
tone="neutral"
/>
</Box>
<Code block className="text-xs">
{snippet}
</Code>
</Stack>Best practices
Use size
64for chat-avatar / panel status; use20for inline text-adjacent cues. They are separate designs, not a scale factor.Prefer
theme="auto"so the orb followsdata-theme/ system preference; pindarkorlightonly when the surrounding surface is fixed.Pass a specific
aria-labelwhen the state text is not otherwise announced (e.g. next to a ThinkingLoader that already speaks).
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| state | OrbState | "working" | Which animation to show. |
| size | OrbSize | 64 | Tuned size preset — 64 (avatar) or 20 (inline). Separate designs, not a scale. |
| theme | OrbTheme | "auto" | Theme mode; |
| speed | number | 1 | Animation speed multiplier on top of the preset's baked speed. |
| paused | boolean | false | Freeze the animation on the current frame. |
| style | CSSProperties | Inline styles for the canvas element. | |
| className | string | Additional CSS class names for the canvas element. | |
| aria-label | string | Accessible name. Overrides the per-state default when provided. |
Also exports
- type
OrbSize - type
OrbState - type
OrbTheme - const
THINKING_ORB_SIZES - const
THINKING_ORB_STATES - interface
ThinkingOrbProps
Composition
Used by