shader-glsl
This skill should be used when the user asks to "write a fragment shader", "make a GLSL gradient/noise/plasma background", "create an image transition (dissolve, displacement, glitch)", "add distortion or chromatic aberration", "build an SDF shape shader", "wire up a Three.js ShaderMaterial with uni
By iart-ai · 512 installs
npx skills add iart-ai/webgl-animation-skills --skill shader-glsl
Source repository · Upstream listing
Shader / GLSL
Write GPU fragment shaders for generative motion, gradients, transitions, and post processing. Fragment shaders run once per pixel in parallel — the most performant way to do full screen generative motion.
When to use
Animated gradient, noise, plasma, or aurora backgrounds.
Image transitions: dissolve, displacement, glitch, ripple, wipe.
Distortion, chromatic aberration, generative patterns, SDF shapes.
Post processing passes over a rendered scene.
Fragment shader skeleton
Every fragment shader computes a color for one pixel. Normalize coordinates, aspect correct, then build color.
The cosine palette line above (Inigo Quilez palettes) is the fastest route to a good looking animated gradient: a + b cos(2π (c t + d)) with tunable a,b,c,d vec3s.
Core building blocks
smoothstep + mix are the workhorses. smoothstep(e0, e1, x) gives a smooth 0→1 ramp; mix(a, b, t) linearly blends. Antialias an edge by the width of one pixel:
Hash + value noise (no textures needed):
Full value/simplex noise and fbm variants are in references/glsl cookbook.md .
SDF shapes give resolution independent crisp geometry. Distance is negative inside, positive outside:
Domain warping for fluid, marbled looks — feed noise into noise:
Image transitions
Sample two textures and blend per pixel by a progress uniform u progress (0→1).
Dissolve / noise wipe — reveal by thresholding noise:
Displacement — push UVs using a displacement map before sampling:
Glitch — block shift rows by time, split RGB channels (chromatic aberration):
Three.js ShaderMaterial wiring
For a full screen pass with a plain camera, write the vertex shader to output position directly and skip projection (as above). For shaders applied to real geometry, pass vUv from the vertex shader via varying vec2 vUv; void main(){ vUv = uv; gl Position = projectionMatrix modelViewMatrix vec4(position,1.0); } .
Mobile / performance
Declare precision mediump float; on mobile when highp is not needed; some effects (large coordinates, deep fbm) require highp .
Cap pixel ratio: renderer.setPixelRatio(Math.min(devicePixelRatio, 2)) . Render to a lower res target and upscale for heavy shaders.
Loops must have constant bounds in GLSL ES — no dynamic loop counts. Keep fbm octaves ≤ 5–6.
Avoid if /branches in hot paths; prefer mix / step / smoothstep . Minimize texture2D calls; avoid dependent texture reads where possible.
WebGL2/GLSL ES 3.00 enables texelFetch , integer ops, and textureLod ; declare version 300 es and use in / out / fragColor .
Deliver & verify (standalone HTML)
Packaged helper ( scripts/ ): scripts/seek shot.sh anim.html 0 1.5 3 freezes the ?t=N harness and screenshots each moment; scripts/contact sheet.sh sheet.png frame .png tiles them for one glance review. See scripts/README.md .
For a self contained shader (gradient/noise background, transition, generative loop) the deliverable is one HTML file that opens directly in a browser — Three.js from a CDN via an importmap, one full screen quad, one render loop, no build step. A single file is the right tier for a shader; don't reach for a bundler when one file does the job.
Output contract:
One .html : importmap pins three to a CDN; the GLSL string, ShaderMaterial , full screen quad, and render loop in one inline <script type="module" .
The shader is a pure function of uniforms — drive everything from u time (and u progress for transitions). All animation flows through one uniform you can pin.
Any in shader randomness already comes from a deterministic hash(uv) — no per frame seeding needed; just don't feed it wall clock outside u time .
Seek/freeze harness — render ONE frame at a fixed time for screenshots. ?t=N sets u time (and optionally u progress ) to N , renders one frame, and stops the loop — a deterministic still.
Verify loop — render → freeze → screenshot → check: open at three instants — start, mid, end ( ?t=0 , ?t=<mid , ?t=<end ; for a transition use u progress 0 / 0.5 / 1) — screenshot each, and check both fidelity (matches the brief) and artifacts : a black/blank canvas = shader compile or parse error (read the console for the GLSL log), banding, NaN blowout (white/garbage pixels from pow / log of negatives), missing texture for transitions (CDN/asset 404). WebGL needs a GPU context; Playwright/Chromium supplies one (swiftshader) headless.
Before you finish:
1. Canvas renders — not black/blank, no shader compile or console errors, no CDN 404s.
2. ?t=N freezes a reproducible frame (same N → same pixels; u time is the only clock).
3. Screenshotted at start / mid / end (or progress 0/0.5/1) — matches the brief, no banding/NaN/black.
4. Disposed and leak free if embedded in an SPA ( material.dispose() , geometry.dispose() , renderer.dispose() , stop the loop).
5. prefers reduced motion honored — freeze u time or slow the animation where motion is decorative.
Quick reference
Goal Primitive
Animated gradient cosine palette a + b cos(...)
Organic texture fbm(uv scale + time)
Crisp shape SDF + smoothstep(fwidth(d), fwidth(d), d)
Fluid / marble domain warp: noise into noise
Reveal transition threshold noise vs u progress
Glitch row hash shift + RGB channel offset
AA edge fwidth(d) for screen space width
Reference files
references/glsl cookbook.md — Full value and simplex noise + fbm implementations, IQ cosine palette recipes, the complete SDF shape library with boolean ops and rounding, domain warping, all three image transitions (dissolve/displacement/glitch) as complete shaders, Three.js uniform/texture wiring, GLSL ES 3.00 migration, and mobile precision gotchas.