Documentation

Dynamic Props

Dynamic props let any numeric or position prop respond to time, mouse position, or the visual output of another layer — declared directly as a prop value.

Instead of passing a static prop value, you can use a dynamic prop config:

<!-- Static value -->
<Circle :radius="0.5" />

<!-- Dynamic prop: animates radius automatically -->
<Circle :radius="{ type: 'auto-animate', mode: 'ping-pong', outputMin: 0.2, outputMax: 0.6 }" />

auto-animate

Continuously animates a numeric prop between two values over time.

<Shader className="w-full h-64">
  <Circle
    color="#6366f1"
    radius={{
      type: 'auto-animate',
      mode: 'ping-pong',
      outputMin: 0.2,
      outputMax: 0.6,
      speed: 0.8,
      easing: 'sine'
    }}
  />
</Shader>

Properties:

PropTypeDefaultDescription
mode'ping-pong' | 'loop'requiredping-pong oscillates between min and max. loop advances from min to max then wraps.
outputMinnumberrequiredValue at the start/bottom of the range.
outputMaxnumberrequiredValue at the end/top of the range.
speednumber1.0Cycles per second. Negative values reverse the loop direction.
easingstring'sine'Easing curve for ping-pong mode. Options: sine, linear, quad, expo, bounce.

Use cases: Pulsing shapes, looping gradient angles, breathing glow intensities, continuous rotation with loop mode and outputMin: 0, outputMax: 360.

mouse-position

Drives an XY position prop from the cursor location. Use this for any prop that expects { x, y } coordinates, such as center, position, or offset.

<Shader className="w-full h-64">
  <Circle
    color="#6366f1"
    radius={0.15}
    center={{
      type: 'mouse-position',
      smoothing: 0.12,
      momentum: 0.2
    }}
  />
</Shader>

Properties:

PropTypeDefaultDescription
x'mouse' | number'mouse''mouse' tracks cursor X. A number pins it to that value (0–1).
y'mouse' | number'mouse''mouse' tracks cursor Y. A number pins it to that value (0–1).
invertXbooleanfalseFlips the X direction — cursor moving right moves position left.
invertYbooleanfalseFlips the Y direction.
smoothingnumber0Lag amount (0–1). 0 = instant, higher values = sluggish follow.
momentumnumber0Spring bounce (0–1). 0 = no overshoot, values near 1 = springy.
reachnumber1Displacement scale. 1 = 1:1 with cursor. 2 = twice the displacement. 0 = pinned to origin.
originXnumber0.5X coordinate of the origin point that displacement scales from.
originYnumber0.5Y coordinate of the origin point.

Pinning one axis: Set x or y to a number to fix that axis while the other tracks the mouse:

<!-- Tracks only horizontal movement, locked at vertical center -->
<Circle :radius="0.15" :center="{ type: 'mouse-position', y: 0.5 }" />

mouse

Drives a single numeric prop from the cursor's X or Y axis position.

<Shader className="w-full h-64">
  <Circle
    color="#6366f1"
    radius={{
      type: 'mouse',
      axis: 'x',
      outputMin: 0.1,
      outputMax: 0.7,
      smoothing: 0.1
    }}
  />
</Shader>

Properties:

PropTypeDefaultDescription
axis'x' | 'y'requiredWhich pointer axis drives this prop.
outputMinnumberrequiredOutput value when axis is at 0 (left or top).
outputMaxnumberrequiredOutput value when axis is at 1 (right or bottom).
curvenumber0Power curve: -1 to +1. Negative = biased toward outputMin, positive = toward outputMax.
smoothingnumber0Lag (0–1). Same as mouse-position.
momentumnumber0Spring bounce (0–1). Same as mouse-position.

Use cases: Blur amount from horizontal position, brightness from vertical position, hue shift, zoom intensity.

map

Drives a numeric prop from the visual output (alpha channel or luminance) of another named layer. The source layer's rendered pixels become a control signal.

First, give the source component an id. Then reference that id in the driver:

<Shader className="w-full h-64">
  <LinearGradient
    id="grad"
    colorA="#000000"
    colorB="#ffffff"
  />
  <DotGrid
    dotSize={{
      type: 'map',
      source: 'grad',
      channel: 'luminance',
      inputMin: 0,
      inputMax: 1,
      outputMin: 0.02,
      outputMax: 0.12
    }}
  />
</Shader>

Properties:

PropTypeDefaultDescription
sourcestringrequiredThe id of the source component to sample from.
channelstringrequiredWhich channel to extract. alpha, alphaInverted, luminance, luminanceInverted.
inputMinnumberrequiredSource values at or below this are treated as 0.
inputMaxnumberrequiredSource values at or above this are treated as 1.
outputMinnumberrequiredOutput value when input = 0.
outputMaxnumberrequiredOutput value when input = 1.
curvenumber0Power curve applied after normalization. -1 to +1.

The map type causes the source layer to be rendered to a texture (RTT) on first use. This is a one-time cost. The source renders independently and its output is sampled each frame.

Combining dynamic props

Multiple dynamic props can be active on the same component — one per prop:

<Shader className="w-full h-64">
  <LinearGradient colorA="#0f172a" colorB="#4f46e5" />
  <LensFlare
    center={{ type: 'mouse-position', smoothing: 0.1 }}
    intensity={{
      type: 'auto-animate',
      mode: 'ping-pong',
      outputMin: 0.4,
      outputMax: 1.0,
      speed: 0.6
    }}
  />
</Shader>

Updating at runtime (JS API)

In the JavaScript API, shader.update() also accepts dynamic prop configs — you can switch between a static value and a dynamic prop at any point:

// Start with a static value
const shader = await createShader(canvas, {
  components: [
    { type: 'Circle', id: 'c', props: { radius: 0.4 } }
  ]
})

// Later: switch to animated
shader.update('c', {
  radius: {
    type: 'auto-animate',
    mode: 'ping-pong',
    outputMin: 0.2,
    outputMax: 0.7
  }
})

// Later: switch back to static
shader.update('c', { radius: 0.4 })