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-orbUsage
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.
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
| Prop | Type | Default |
|---|---|---|
| state | "idle" | "listening" | "thinking" | "speaking"Springs between states. | "idle" |
| size | numberCSS px. Fills the parent if omitted. | 100% |
| palette | OklchStop[]2–5 OKLCH stops, top to bottom. | Sky |
| params | Partial<OrbParams>Motion, surface and rim. | DEFAULT_PARAMS |
| audio | "mic" | "simulated"Listening source. | "mic" |
| voice | HTMLMediaElement | MediaStream | AudioNodeSpeaking source. | — |
| level | number | () => number0–1, read every frame. Overrides audio. | — |
| motion | Partial<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" |
| reactiveRim | booleanRim reacts to audio. | false |
| stateParams | StateParamsPer-state param overrides. | DEFAULT_STATE_PARAMS |
| variantParams | VariantParamsPer-variant param overrides. | DEFAULT_VARIANT_PARAMS |
| transitionSpeed | numberLower is slower. | 1 |
| fps | numberFrame cap. 30 below 48px. | 60 |
| seed | numberFixes the animation phase. | random |
| className, style | string, CSSPropertiesApplied to the canvas. | — |
| aria-label | string | "AI assistant, {state}" |