# Thinking Orbs
> Animated thinking orbs for AI agent UIs: an open-source React component library (plus plain JS and SwiftUI) with a state for each thing an agent does, like thinking, searching and compacting. No dependencies, built to read at 20px.
Website: https://thinkingorbs.com
## Installation
Install the package:
```bash
npm i @yogesharc/thinking-orbs
# or: pnpm add @yogesharc/thinking-orbs, yarn add @yogesharc/thinking-orbs, bun add @yogesharc/thinking-orbs
```
Or copy the React source into the project with shadcn, to own and edit it. It lands in `components/`, so import from `@/components/orb`:
```bash
npx shadcn@latest add https://thinkingorbs.com/r/orb.json
```
The React orb needs nothing but React; the plain JS one needs nothing at all.
For SwiftUI (iOS 17, macOS 14), add the Swift package. In Xcode: File › Add Package Dependencies, paste https://github.com/yogesharc/thinking-orbs, and add `ThinkingOrbs` to the app target. Or in a Package.swift:
```swift
// Package.swift
.package(
url: "https://github.com/yogesharc/thinking-orbs",
from: "0.1.2"
),
// and in your target's dependencies:
.product(name: "ThinkingOrbs", package: "thinking-orbs"),
```
## Usage
### React
```tsx
import { Orb } from "@yogesharc/thinking-orbs";
export function Thinking() {
return (
Thinking
);
}
```
### Without React
```js
import { mountOrb } from "@yogesharc/thinking-orbs/vanilla";
// Draws into any on the page, in its CSS color.
const orb = mountOrb(document.querySelector("svg"), { state: "reasoning", label: "Thinking" });
orb.pause(); // hold it on its frame
orb.play(); // carry on
orb.destroy(); // remove it
```
### SwiftUI
```swift
import SwiftUI
import ThinkingOrbs
struct Thinking: View {
var body: some View {
HStack(spacing: 8) {
Orb(state: .reasoning)
Text("Thinking")
}
}
}
```
## Props
All optional. `paused` and `className` are React only; `mountOrb` returns `pause()` and `play()` instead, and takes its color from the svg's CSS `color`.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `state` | `OrbState` | `"idle"` | What the agent is doing. |
| `variant` | `OrbVariant` | `"default"` | Which look of that state. |
| `size` | `number` | `20` | Width and height in px. |
| `speed` | `number` | `1` | Speed multiplier. |
| `shape` | `OrbShape` | — | Another form, from @yogesharc/thinking-orbs/shapes. |
| `render` | `OrbRender` | — | Another way to draw it, from @yogesharc/thinking-orbs/renders. |
| `density` | `number` | `1` | Dot count multiplier. |
| `dotSize` | `number` | `1` | Dot size multiplier. |
| `tilt` | `number` | `20` | Viewing angle from above, in degrees. |
| `direction` | `OrbDirection` | — | Where using clicks, or which way its scroll goes. Anywhere without one. |
| `paused` | `boolean` | `false` | Freezes the animation. |
| `label` | `string` | — | Name for screen readers. |
| `className` | `string` | — | Tint it with text-* classes. |
The orb draws in the text color (`currentColor`). With reduced motion it holds still.
In SwiftUI, `Orb(...)` takes the same options as typed parameters, minus `shape`, `render` and `className`, all optional: `state: OrbState = .idle`, `variant: OrbVariant = .default`, `size: CGFloat = 20`, `speed: Double = 1`, `density: Double = 1`, `dotSize: Double = 1`, `tilt: Double = 20`, `direction: OrbDirection? = nil`, `paused: Bool = false`, `label: String? = nil`. It draws in the foreground style, so `.foregroundStyle(.blue)` tints it, and with Reduce Motion it holds still. Shapes and renders are web-only for now.
## Shapes and renders
The core ships one shape and one render. Other shapes and ways of drawing it are opt-in modules, so only what you import lands in the bundle. With shadcn, add them as `https://thinkingorbs.com/r/orb-shapes.json` and `https://thinkingorbs.com/r/orb-renders.json`.
```tsx
import { Orb } from "@yogesharc/thinking-orbs";
import { cube } from "@yogesharc/thinking-orbs/shapes";
import { halftone } from "@yogesharc/thinking-orbs/renders";
```
- `@yogesharc/thinking-orbs/shapes`: `cube`, `octahedron`, `tetrahedron`, `torus`.
- `@yogesharc/thinking-orbs/renders`: `dashes`, `squares`, `crosses`, `mesh`, `halftone`, `lines`, `verticalLines`.
Not every state suits every shape or render. A custom shape is an `OrbShape`, `{ points(count, look) }` returning [x, y, z] points inside the unit sphere; a custom render is an `OrbRender`, whose `mount` makes SVG elements and returns `dot(i, x, y, r, a, dx, dy)`. Define either outside the component.
## States
- `working`: Busy, running a tool.
- `default`: A ring of light runs down it.
- `gyro`: Wobbles like a spinning top.
- `reasoning`: Thinking.
- `default`: One spark wanders over it.
- `twins`: Two sparks wander at once.
- `searching`: Searching the web or files.
- `default`: A lens hops between spots.
- `lighthouse`: A beam sweeps round, like a lighthouse.
- `background`: Background tasks running, like a dev server.
- `default`: Fewer, bigger dots.
- `spiral`: The dots wound into spiral arms.
- `using`: Using a browser or computer, as a person would.
- `default`: An arrow drawn in the dots glides about and clicks. With a `direction`, it clicks out on that side, like a back button for `"left"`.
- `tap`: A glowing cursor glides about, clicks, and sends a ripple out.
- `scroll`: Rolls in flicks that coast to a stop, the way `direction` scrolls a page. Without one, each flick goes a new way.
- `type`: Lines of text type out across it, word by word, after a caret.
- `zoom`: The face swells toward you and eases back.
- `snap`: Stops dead with a flash, like a screenshot, then turns on.
- `record`: Pulses dim and bright, like a recording light.
- `retrying`: Retrying after an error.
- `default`: Spins, then winds back.
- `surge`: Each turn launches fast and eases out.
- `compacting`: Compacting the context window.
- `default`: Packs tight, then springs back past loose.
- `squeeze`: Packs while wringing the top against the bottom.
- `fuse`: Packs along a burning fuse line, with no bounce.
- `waiting`: Waiting for a usage limit to reset.
- `default`: A comet spirals round it.
- `idle`: Idle, or anything without its own state. It was called `base`, which still works.
- `default`: A plain spin.
- `blink`: Blinks now and then, its lids closing from top and bottom.
## About
- Built by Yogesh: https://yogesharc.com
- X: https://x.com/yogesharc
- Sponsor: https://www.patreon.com/c/yogesharc
- GitHub: https://github.com/yogesharc/thinking-orbs
- License: MIT