Core Concepts

Shader lifecycle

ShaderPad has the following render lifecycle:

  1. Construct a shader using new ShaderPad()
  2. Initialize custom uniforms or textures using initializeUniform() and initializeTexture()
  3. Render with play(), step(), or draw()
  4. Clean up with destroy()

You can go over the details of each method in the Methods API reference. Below is a quick overview, including when you may want to use each method.

Constructor

const shader = new ShaderPad(fragmentShaderSrc, { canvas })

Rendering Methods

Quick Reference

  • Use play() for animation loops
  • Use step() for manual time/frame advancement
  • Use draw() when time, frame, and history should remain unchanged

play(onBeforeStep?)

play() starts the animation loop. u_time and u_frame uniforms are updated automatically, and history is kept up to date.

shader.play((time, frame) => {
  shader.updateUniforms({ u_speed: Math.sin(time) })
})

Use it when:

  • You want to animate the shader over time
  • You don’t need manual control over timing
  • You’re rendering a single shader or a straightforward rendering pipeline

step(options?)

step() advances exactly one frame, and renders without triggering the animation loop. u_time and u_frame uniforms are updated automatically, and history is kept up to date.

shader.step({ skipHistoryWrite: true })

Use it when:

  • You want deterministic manual control over the animation frame
  • Another loop owns timing
  • You are building a chained or offscreen pipeline

draw(options?)

draw() renders without updating u_time, u_frame, or history.

shader.draw({ skipClear: true })

Use it when:

  • The current uniforms already represent the exact state you want
  • The output should not count as a new animation step

Pause, Reset, Destroy

  • pause() stops the animation loop started by play()
  • resetFrame() resets the clock and frame counter
  • reset() resets the clock and frame counter, and also clears history buffers
  • destroy() releases WebGL resources and event listeners