| name | shaders-com |
| description | Build reusable canvas-based shaders and animated WebGL effects with shaders.com components in frontend projects. Use when adding shader backgrounds, decorative canvas effects, animated hero sections, or layered visual treatments, composing shader nodes, wiring `shaderRenderer` and `createUniformsMap`, or porting a shaders.com setup into Astro, React, Next.js, or plain browser code. |
| metadata | {"tags":"shaders, shaders.com, webgl, canvas, threejs, frontend, react, nextjs, astro, animation"} |
Shaders.com
Overview
Use shaders.com as a layered rendering system: mount a canvas, initialize a renderer, register a root node, then stack visual nodes in render order. Prefer reusable composition patterns over copying a specific effect.
Quick Start
- Install runtime deps:
shaders and three.
- Add a canvas inside the section that should own the effect.
- Size the canvas to fill its container and keep it non-interactive unless the design needs pointer input.
- Initialize
shaderRenderer() against that canvas.
- Register a root passthrough node first.
- Add effect nodes one by one with explicit uniforms and render order.
- Start animation after all nodes are registered.
Integration Pattern
Use this structure in any framework:
import { shaderRenderer, createUniformsMap } from 'shaders/core'
import { vec4 } from 'three/tsl'
import LinearGradient from 'shaders/core/LinearGradient'
import Blob from 'shaders/core/Blob'
import FilmGrain from 'shaders/core/FilmGrain'
async function initShader(canvas: HTMLCanvasElement) {
const renderer = shaderRenderer()
await renderer.initialize({ canvas, colorSpace: 'p3-linear' })
const rootId = 'shader_root'
renderer.registerNode(
rootId,
({ childNode }: { childNode: any }) => childNode || vec4(0, 0, 0, 0),
null,
null,
{},
)
const baseId = 'shader_base'
const baseUniforms = createUniformsMap(
LinearGradient,
{
colorA: '#0f172a',
colorB: '#1e293b',
colorSpace: 'oklch',
edges: 'mirror',
start: { x: 0.2, y: 0.8 },
end: { x: 0.8, y: 0.2 },
angle: 0,
},
baseId,
)
renderer.registerNode(
baseId,
LinearGradient.fragmentNode,
rootId,
{ blendMode: 'normal', visible: true, renderOrder: 0 },
baseUniforms,
LinearGradient,
)
const motionId = 'shader_motion'
const motionUniforms = createUniformsMap(
Blob,
{
colorA: '#2563eb',
colorB: '#14b8a6',
colorSpace: 'oklch',
size: 1,
deformation: 0.8,
softness: 2,
speed: 1,
highlightIntensity: 0.2,
highlightX: 0.3,
highlightY: -0.2,
highlightZ: 0.4,
highlightColor: '#ffffff',
seed: 1,
center: { x: 0.5, y: 0.5 },
},
motionId,
)
renderer.registerNode(
motionId,
Blob.fragmentNode,
rootId,
{ blendMode: 'normal-oklch', visible: true, renderOrder: 1 },
motionUniforms,
Blob,
)
const grainId = 'shader_grain'
const grainUniforms = createUniformsMap(
FilmGrain,
{ strength: 0.06 },
grainId,
)
renderer.registerNode(
grainId,
FilmGrain.fragmentNode,
rootId,
{ blendMode: 'normal', visible: true, renderOrder: 2 },
grainUniforms,
FilmGrain,
)
renderer.startAnimation()
return renderer
}
Composition Recipe
Build effects as layers, not as one giant configuration blob.
- Base layer:
LinearGradient, flat color, or other low-frequency structure.
- Motion layer:
Blob, noise, waves, or any animated focal element.
- Texture layer: grids, dots, or grain for surface detail.
- Distortion layer:
Liquify or similar post-processing warp.
- Finishing layer: subtle grain or highlights to avoid a flat result.
Tune in this order: palette, contrast, motion speed, distortion strength, then texture intensity.
Framework Notes
- Astro: place the canvas in the component and initialize in a
<script> block near it.
- React or Next.js: hold the canvas in a
ref, initialize in useEffect, and clean up on unmount if the renderer exposes teardown methods.
- Plain HTML or JS: wait until the canvas exists in the DOM before initializing.
Always make the canvas match the layout context around it. Common positioning is position:absolute; inset:0; width:100%; height:100%; display:block; inside a relatively positioned parent.
Guardrails
- Use underscores in node IDs. Hyphens can become invalid GLSL uniform names.
- Pass all shader props explicitly to
createUniformsMap(...). Do not rely on implicit defaults for transformed props such as colors, angles, positions, or compile-time options.
- Set required compile-time props explicitly, such as
edges when a shader node depends on it.
- Guard missing canvases and failed initialization with clear errors.
- Start with 2-4 nodes, then add complexity only after the base composition works.
- Keep the shader decorative unless the product specifically needs interaction.
Styling Guidance
- Match the surrounding product instead of importing the original demo look.
- Let one layer own the mood and let the others support it.
- Keep grain, dot grids, and distortion subtle; they should read as texture, not noise.
- Prefer one strong motion layer over several competing animated layers.
- Test readability with real foreground content on top of the shader.
Troubleshooting
- Nothing renders: verify the canvas exists, the parent has size, and
initialize(...) completed.
- Shader compiles badly: check node IDs, explicit props, and imported shader modules.
- Colors look off: confirm
colorSpace, blend mode, and the color model expected by each node.
- Effect overwhelms content: lower contrast, reduce animation speed, or remove one layer.
- React hydration issues: ensure browser-only initialization runs after mount.