prism-orb

A glass prism orb for AI interfaces. Real-time WebGL, zero dependencies.

Installation

Zero dependencies. React 18+ for the component.

pnpm add prism-orb

Usage

Works out of the box. Every orb on the page shares one WebGL context.

import { PrismOrb, type OrbState } from "prism-orb";

export function Assistant({ state }: { state: OrbState }) {
  return <PrismOrb state={state} size={160} />;
}

States

Set the state prop. The orb eases into the new motion.

Faster, denser motion.

<PrismOrb state="thinking" />

Audio

Simulated until a source is connected. Audio stays in the browser.

import { PrismOrb, orbMic } from "prism-orb";

// Listening follows the mic. Start it from a user gesture.
await orbMic.start();
<PrismOrb state="listening" />

// Speaking follows the AI's voice: <audio>, MediaStream or AudioNode.
<PrismOrb state="speaking" voice={ttsAudio} />

// Or pass your own 0–1 level, as a number or a function.
<PrismOrb state="speaking" level={volume} />

Without React

Same orb, no React. Import from prism-orb/vanilla.

import { createOrb, defineOrbElement } from "prism-orb/vanilla";

const orb = createOrb(document.querySelector("#orb"), { state: "idle" });
orb.set({ state: "speaking" });

// Or as a custom element
defineOrbElement();
// <prism-orb state="thinking" size="160"></prism-orb>

Customize

Tune it, then copy as JSX or JSON.

Palette
Motion
Speed0.90
Flow amount0.65
Direction90°
Drift0.35
Frequency0.40
State intensity1.00
Surface
Dispersion0.50
Softness0.40
Grain0.10
Tone map0.15
Rim
Rim0.15
Rim softness0.10
White spread0.10
Vignette0.08
Audio
Reactivity1.25
Listening
Starts on first interaction
Speaking
Simulated assistant voice
import { PrismOrb, orbMic } from "prism-orb";

// Start from a user gesture, e.g. a mic button click.
await orbMic.start();

<PrismOrb
  state="listening"
  size={200}
  palette={[
    { l: 0.88, c: 0.08, h: 168 },
    { l: 0.78, c: 0.1, h: 192 },
    { l: 0.96, c: 0.03, h: 150 },
    { l: 0.7, c: 0.1, h: 212 },
  ]}
/>

API

PropTypeDefault
state"idle" | "listening" | "thinking" | "speaking"

Springs between states.

"idle"
sizenumber

CSS px. Fills the parent if omitted.

100%
paletteOklchStop[]

2–5 OKLCH stops, top to bottom.

Sky
paramsPartial<OrbParams>

Motion, surface and rim.

DEFAULT_PARAMS
audio"mic" | "simulated"

Listening source.

"mic"
voiceHTMLMediaElement | MediaStream | AudioNode

Speaking source.

—
levelnumber | () => number

0–1, read every frame. Overrides audio.

—
motionPartial<OrbMotion>

Motion variant per state.

DEFAULT_MOTION
field"noise" | "sine" | "ripple" | "spiral"

How the color flow is displaced.

"noise"
noise"simplex" | "perlin" | "value" | "worley"

Used by the noise field.

"simplex"
reactiveRimboolean

Rim reacts to audio.

false
stateParamsStateParams

Per-state param overrides.

DEFAULT_STATE_PARAMS
variantParamsVariantParams

Per-variant param overrides.

DEFAULT_VARIANT_PARAMS
transitionSpeednumber

Lower is slower.

1
fpsnumber

Frame cap. 30 below 48px.

60
seednumber

Fixes the animation phase.

random
className, stylestring, CSSProperties

Applied to the canvas.

—
aria-labelstring"AI assistant, {state}"