React
The ShaderCanvas component and the useShaderRenderer hook.
<ShaderCanvas />
import { ShaderCanvas } from "shadertown/react";| Prop | Type | Default | Notes |
|---|---|---|---|
shader | ShaderName | none | Catalogue slug, for example "aurora" |
preset | ShaderConfig | none | A defineShader result; other props override it |
params | ShaderParams<K> | registry defaults | Typed against the chosen shader |
play | "always" | "visible" | "hover" | "never" | "visible" | When animation is allowed |
quality | { dpr?, fps?, tier? } | { dpr: [1, 2] } | Resolution ceiling, frame-rate cap, and a pinned quality tier; omit tier to follow the page's setQualityTier |
onQualityChange | (state) => void | — | The tier on screen and why, whenever it changes |
startTime | number | 0 | Clock offset, so a paused card is not stuck on frame zero |
trackPointer | boolean | false | Feeds the pointer uniform; costs one listener |
badge | boolean | BadgeCorner | page settings | The credit. false removes it; a corner name moves it |
fallback | ReactNode | none | Rendered instead of the canvas when WebGPU is missing |
onStatusChange | (status) => void | none | pending, ready, unsupported, error |
className | string | none | Applied to the element wrapping the canvas, which is the same box |
style | CSSProperties | none | Applied to the element wrapping the canvas, which is the same box |
aria-hidden | boolean | none | Set it when the shader is pure decoration |
Params do not remount
Changing params calls setParams on the live renderer. Dragging a slider
never recompiles a pipeline or recreates a surface, so a control panel bound
straight to component state is fine.
Changing shader, though, does create a new effect. Treat it as a heavier
operation and give the canvas a key if you want a clean reset.
Reduced motion
ShaderCanvas reads prefers-reduced-motion and holds a still frame when the
user has asked for less movement. The canvas still shows the shader; it just
stops moving.
useShaderRenderer
The escape hatch, for when you want to drive the renderer from your own animation code: a scroll handler, a GSAP timeline, an XR frame loop.
const ref = useRef<HTMLCanvasElement>(null);
const renderer = useShaderRenderer(ref, myPreset);
useEffect(() => {
const onScroll = () => renderer?.setParams({ speed: window.scrollY / 2000 });
window.addEventListener("scroll", onScroll, { passive: true });
return () => window.removeEventListener("scroll", onScroll);
}, [renderer]);