Shader starter kit

The Shader Starter Kit demonstrates how to integrate WebGL shaders with [tldraw](https://github.com/tldraw/tldraw). It creates dynamic backgrounds that respond to canvas interactions, and includes four complete examples and a reusable `WebGLManager` base class for building custom shader effects.

To build with a shader starter kit, run this command in your terminal:

npm create tldraw@latest -- --template shader

Use cases

Use the shader starter kit to build:

Immersive creative tools

Layer animated, touch-responsive backgrounds behind your drawing UI for design or storytelling apps.

Data-rich dashboards

Highlight state changes or live metrics using GPU-driven gradients and particle systems that react to editor data.

Games and interactive experiences

Combine tldraw shapes with shader-based effects to build puzzle overlays, interactive maps, or ambient scenes.

Live events and streaming overlays

Drive real-time graphics for broadcast overlays or virtual stages that react to presenter actions.

Educational demos

Teach graphics concepts with interactive examples that expose shader parameters through the config panel.


How it works

WebGLManager lifecycle

The reusable WebGLManager class (src/WebGLManager.ts) creates and manages a WebGL2 context that is synchronized with the tldraw canvas. It owns the render loop and exposes lifecycle hooks—onInitialize(), onUpdate(), onRender(), and onDispose(). Each shader manager can focus on its effect-specific logic while sharing viewport coordination, resolution control, and animation timing. The Minimal, Rainbow, and Shadows examples extend this base class; the Fluid example uses a different architecture with its own simulation system.

Config panel system

The config panel components (src/config-panel/) provide ready-made UI controls for editing shader uniforms. Each panel stores settings in reactive atoms and persists them to localStorage, so your shader parameters survive reloads without extra wiring.

The template ships with four complete demos that follow the same pattern of manager, renderer, config panel, and GLSL files:

  • Fluid simulation (src/fluid/) — Navier-Stokes-based flow that turns pointer movement into velocity splats. Includes an in-depth guide.
  • Rainbow (src/rainbow/) — Gradient animation that demonstrates time-based uniforms and color cycling.
  • Shadows (src/shadow/) — Raymarched shadow effect using signed distance fields derived from canvas geometry.
  • Minimal (src/minimal/) — Dark-mode-aware solid color shader that makes it easy to start your own effect, with a step-by-step walkthrough.

Switch between demos from the example menu next to the style panel to see how different managers plug into the same infrastructure.

Fluid simulation

A real-time fluid simulation based on Pavel Dobryakov's WebGL fluid implementation. Shape movements create dynamic flows in the simulation.

  • Navier-Stokes fluid dynamics
  • Shape movements create velocity-based splats
  • Configurable physics and visual effects

See: src/fluid/ | Documentation

Rainbow

An animated gradient shader demonstrating time-based effects and uniform management.

See: src/rainbow/

Shadows

Dynamic shadow casting from tldraw shapes using raymarching and signed distance fields.

See: src/shadow/

Minimal

A bare-bones template for starting new shader projects. Renders a solid color that adapts to dark mode.

See: src/minimal/ | Documentation


Customization

Start from the minimal template

Copy the minimal shader to create a new effect:

cp -r src/minimal src/my-shader

Then update:

  • config.ts — Define uniforms and UI controls for your shader.
  • fragment.glsl / vertex.glsl — Implement rendering logic.
  • MyShaderManager.ts — Extend WebGLManager and coordinate buffers, uniforms, and lifecycle hooks.
  • MyRenderer.tsx — Mount the manager from React and handle cleanup.
  • MyConfigPanel.tsx — Customize the controls exposed to users.

Finally, register the new manager in src/App.tsx.

Extend WebGLManager hooks

Each manager can override lifecycle hooks to add render targets, resize behavior, or post-processing passes. Use onInitialize() to set up buffers, textures, and framebuffers; onUpdate() for per-frame uniform updates (such as time, resolution, or editor-driven state); and onRender() to issue draw calls. The base class also exposes helpers for handling pixel density and canvas resizes.

Integrate with tldraw data

Every manager receives the live tldraw editor instance, which lets you:

  • Access shapes with editor.getCurrentPageShapes().
  • Subscribe to document changes through editor.store.listen().
  • Read the current camera via editor.getCamera().
  • Convert coordinates with editor.pageToViewport().
  • Track pointer data from editor.inputs.getCurrentScreenPoint().

See src/fluid/FluidManager.ts for a full example of mixing editor state with GPU simulation.

Resources


Further reading

For multi-user shader environments, see the Multiplayer Starter Kit. To create custom shapes with advanced geometry and rendering, see Shape Utilities. For working with tldraw's reactive state system and event handling, see Editor State Management.


Building with this starter kit?

If you build something great, please share it with us in our #show-and-tell channel on Discord. We want to see what you've built!

Prev
Multiplayer starter kit
Next
Accessibility