Skip to content
← Inventory

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 64 for chat-avatar / panel status; use 20 for inline text-adjacent cues. They are separate designs, not a scale factor.

  • Prefer theme="auto" so the orb follows data-theme / system preference; pin dark or light only when the surrounding surface is fixed.

  • Pass a specific aria-label when the state text is not otherwise announced (e.g. next to a ThinkingLoader that already speaks).

Props

PropTypeDefaultDescription
stateOrbState"working"

Which animation to show.

sizeOrbSize64

Tuned size preset — 64 (avatar) or 20 (inline). Separate designs, not a scale.

themeOrbTheme"auto"

Theme mode; auto detects from the host project.

speednumber1

Animation speed multiplier on top of the preset's baked speed.

pausedbooleanfalse

Freeze the animation on the current frame.

styleCSSProperties

Inline styles for the canvas element.

classNamestring

Additional CSS class names for the canvas element.

aria-labelstring

Accessible name. Overrides the per-state default when provided.

Also exports

  • typeOrbSize
  • typeOrbState
  • typeOrbTheme
  • constTHINKING_ORB_SIZES
  • constTHINKING_ORB_STATES
  • interfaceThinkingOrbProps

Composition