Quickstart (JavaScript)
Shaders provides a vanilla JavaScript API that works with any bundler-based JS environment (Vite, Webpack, etc.), Web Components, or plain HTML with an ESM-compatible setup — no framework required.
Install the npm package
npm install shaders
Set up a canvas
Create a <canvas> element in your HTML and give it a size:
<canvas id="my-shader" style="width: 100%; height: 400px;"></canvas>
Your first shader
Import createShader, pass it the canvas and a list of components:
import { createShader } from 'shaders/js'
const canvas = document.getElementById('my-shader')
const shader = await createShader(canvas, {
components: [
{ type: 'LinearGradient', props: { colorA: '#0f172a', colorB: '#7c3aed' } },
{ type: 'CursorTrail', props: {} }
]
})
The components array defines your visual layers, evaluated top to bottom, blended together on the GPU. In this example, we render the linear gradient first, and then the cursor trail effect on top of that.
Sizing the canvas
The <canvas> has no intrinsic size. Set dimensions via CSS — the shader tracks them automatically via ResizeObserver:
<!-- Fixed size -->
<canvas id="shader" style="width: 600px; height: 400px;"></canvas>
<!-- Full viewport -->
<canvas id="shader" style="width: 100vw; height: 100dvh;"></canvas>
<!-- Fluid with aspect ratio (using Tailwind or equivalent CSS) -->
<canvas id="shader" class="w-full aspect-video"></canvas>
If you resize the canvas programmatically via JavaScript after initialization, call shader.resize() to sync:
canvas.style.height = '600px'
shader.resize()
Configuring components
Props are passed in the props object for each component. All prop names are camelCase:
const shader = await createShader(canvas, {
components: [
{
type: 'LinearGradient',
props: {
colorA: '#ff6b6b',
colorB: '#4ecdc4',
angle: 45,
colorSpace: 'oklch'
}
}
]
})
Updating at runtime
Assign an id to any component, then use shader.update() to change its props at any time:
const shader = await createShader(canvas, {
components: [
{ type: 'LinearGradient', id: 'gradient', props: { colorA: '#0f172a', colorB: '#7c3aed', angle: 0 } }
]
})
// Hook up a slider
document.querySelector('#angle-slider').addEventListener('input', e => {
shader.update('gradient', { angle: Number(e.target.value) })
})
When update() is called, the shader updates on the GPU instantly — no recompilation, no flicker.
Browse the Component Docs for the full prop reference on every component.
Cleanup
When you're done, call destroy() to free GPU resources:
shader.destroy()
Always destroy shaders when navigating away — for example, in a single-page app's route change handler.
Next steps
- Composing Effects — stack and nest components for complex results
- Layout & Positioning — position the canvas in your UI
- Dynamic Props — animate and mouse-track props declaratively
- Component Docs — browse all available shaders