An orb is driven by four kinds of input: parameters, colors, a mood (state), and two volumes. All of them update live, and all of them ease toward their new value instead of jumping.
Parameters#
Each orb defines its own parameters. ORB-01, for example, has speed, spin, turb, exposure and about fifteen more.
You pass the ones you want to change:
<ShaderOrb orb={orb01} params={{ speed: 1.5, turb: 0.6 }} />- Anything you don't pass falls back to the current mood's preset, then to the orb's default.
- Values are clamped to the range the orb declares. Many shaders use a parameter as a divisor or a camera distance, so an out-of-range value would break the image rather than just look different.
- Every orb's keys, defaults and ranges are listed on its page, and are also on the definition itself:
orb01.params.map((p) => `${p.key}: ${p.default} (${p.min} to ${p.max})`);Ranges are not linear#
Some ranges span orders of magnitude (ORB-01's exposure runs from 1.5 to 1500, with a default of 60). If you build your
own sliders, don't pick random values across the whole range: moving exposure from 60 to 900 turns the orb nearly
black. Nudge relative to the current value instead, and leave camera and framing parameters (camDist, focal,
envRadius and similar) alone unless you mean to change the shape.
Integrated parameters#
Parameters marked integrated on an orb's page, such as speed and spin, are phase clocks. The shader doesn't read
the value directly; it accumulates it over time. That means changing speed changes how fast things move without making
the animation jump.
Colors#
<ShaderOrb orb={orb01} colors={{ tint: "#ff6688" }} />Colors are hex strings (#rgb or #rrggbb). An invalid value falls back to white. Each orb lists its color keys.
Moods#
<ShaderOrb orb={orb01} state="thinking" />state is one of idle, thinking or speaking. Choosing one eases every parameter (and the colors, for orbs that
define state colors) toward that mood's preset. Parameters you pass explicitly still win over the preset.
You can replace an orb's presets entirely:
<ShaderOrb
orb={orb01}
state="speaking"
statePresets={{ speaking: { turb: 1.2, speed: 2 } }}
stateColors={{ speaking: { tint: "#88ff99" } }}
/>Volumes#
<ShaderOrb orb={orb01} volumes={{ input: 0.4, output: 0.8 }} />Two numbers from 0 to 1 that the shaders read as "how loud is the input" and "how loud is the output". Most orbs use
input to stir the interior and output to brighten and speed the flow. They ease toward the value you give them, so you
can feed them a raw level every frame without it looking jittery.
If you don't pass volumes, each mood synthesizes its own gentle motion (speaking pulses, thinking drifts, idle rests).
stateVolumes lets you set per-mood levels:
<ShaderOrb orb={orb01} state="speaking" stateVolumes={{ speaking: { input: 0.2, output: 0.9 } }} />Updating without re-rendering: live#
Props flow through React, so changing them re-renders the component that owns them. For values that change many times per
second, such as a slider being dragged, pass a live function instead. The orb calls it every frame and reads from
whatever it returns:
const store = useRef<OrbValues>({ state: "idle", params: {}, colors: {} }).current;
<ShaderOrb orb={orb01} live={() => store} />
// anywhere, no re-render:
store.params!.speed = 2;The example app uses this for every slider; it kept drags smooth when the same updates through setState did not.