npx skills add ...
npx skills add bfollington/terma --skill shadertoy
This skill should be used when working with Shadertoy shaders, GLSL fragment shaders, or creating procedural graphics for the web. Use when writing .glsl files, implementing visual effects, creating generative art, or working with WebGL shader code. This skill provides GLSL ES syntax reference, common shader patterns, and Shadertoy-specific conventions.
npx skills add bfollington/terma --skill shadertoy
Shadertoy is a platform for creating and sharing GLSL fragment shaders that run in the browser using WebGL. This skill provides comprehensive guidance for writing shaders including GLSL ES syntax, common patterns, mathematical techniques, and best practices specific to real-time procedural graphics.
Activate this skill when:
.glsl shader filesEvery Shadertoy shader implements the mainImage function:
Always available in shaders:
| Type | Name | Description |
|---|---|---|
vec3 | iResolution | Viewport resolution (x, y, aspect ratio) |
float | iTime | Current time in seconds (primary animation driver) |
float | iTimeDelta | Time to render one frame |
int | iFrame | Current frame number |
vec4 | iMouse | Mouse: xy = current position, zw = click position |
sampler2D | iChannel0-iChannel3 | Input textures/buffers |
vec3 | iChannelResolution[4] | Resolution of each input channel |
vec4 | iDate | Year, month, day, time in seconds (.xyzw) |
Standard patterns for normalizing coordinates:
Use Inigo Quilez's cosine palette for smooth color gradients:
Simple 2D hash for noise and randomness:
Standard pattern for 3D rendering via sphere tracing:
2D rotation:
3D axis-angle rotation (modifies in-place):
Create fractal-like structures:
Vignette:
Film grain/dithering (reduces banding):
Gamma correction:
For complex effects requiring temporal feedback or multiple rendering stages:
ALWAYS follow these rules to avoid compilation errors:
f suffix: Use 1.0 NOT 1.0fsaturate(): Use clamp(x, 0.0, 1.0) insteadpow(max(x, 0.0), p), sqrt(abs(x))find, grep - use Glob/Grep tools insteadiTime for temporal variationVisualizing complex numbers:
references/common-patterns.mdcx_log(), cx_pow(), or polynomial evaluationRay marching 3D scenes:
map() functionro, ray direction rd)Creating noise/organic effects:
hash21() for random valuesfbm() (fractional Brownian motion) for natural variationsin()/cos() for structured patternsMulti-layer composition:
mix() or custom blend modessmoothstep() for soft transitionsVisualize intermediate values:
Simplify progressively:
Check for NaN/Inf:
if (isnan(value) || isinf(value)) return vec3(1.0, 0.0, 0.0);mix(), step(), smoothstep() instead of ifmediump or lowp where appropriate (mobile)Based on observed patterns in creative work:
my-shader-name.glslWhen forking or remixing shaders:
Complete GLSL ES syntax reference including:
Search with: Read /references/glsl-reference.md for complete language reference.
Comprehensive pattern library including:
Search with: Grep "pattern" references/common-patterns.md for specific techniques.
Reference implementation showing:
sin(iTime), mod(iTime, period), smoothstep() transitions// Aspect-corrected UV centered at origin (-1 to 1, aspect-preserved)
vec2 uv = (fragCoord.xy - 0.5 * iResolution.xy) / min(iResolution.y, iResolution.x);
// Alternative compact form:
vec2 uv = (fragCoord * 2.0 - iResolution.xy) / min(iResolution.x, iResolution.y);
// Simple normalized (0 to 1)
vec2 uv = fragCoord / iResolution.xy;vec3 palette(float t, vec3 a, vec3 b, vec3 c, vec3 d) {
return a + b * cos(6.28318 * (c * t + d));
}
// Example usage:
vec3 col = palette(
t,
vec3(0.5, 0.5, 0.5), // base
vec3(0.5, 0.5, 0.5), // amplitude
vec3(1.0, 1.0, 0.5), // frequency
vec3(0.8, 0.90, 0.30) // phase
);float hash21(vec2 p) {
p = fract(p * vec2(234.34, 435.345));
p += dot(p, p + 34.23);
return fract(p.x * p.y);
}// Distance field function
float map(vec3 p) {
return length(p) - 1.0; // Sphere at origin, radius 1
}
// Normal calculation
vec3 calcNormal(vec3 p) {
vec2 e = vec2(0.001, 0.0);
return normalize(vec3(
map(p + e.xyy) - map(p - e.xyy),
map(p + e.yxy) - map(p - e.yxy),
map(p + e.yyx) - map(p - e.yyx)
));
}
// Ray marching loop
vec3 render(vec3 ro, vec3 rd) {
float t = 0.0;
for (int i = 0; i < 100; i++) {
vec3 p = ro + rd * t;
float d = map(p);
if (d < 0.001) {
// Hit - calculate lighting
vec3 n = calcNormal(p);
return n * 0.5 + 0.5; // Normal visualization
}
if (t > 10.0) break;
t += d * 0.5; // Step (0.5 factor for safety)
}
return vec3(0.0); // Miss
}mat2 rot2d(float a) {
float c = cos(a), s = sin(a);
return mat2(c, -s, s, c);
}
// Usage: p.xy *= rot2d(iTime);void rot(inout vec3 p, vec3 axis, float angle) {
axis = normalize(axis);
float s = sin(angle), c = cos(angle), oc = 1.0 - c;
mat3 m = mat3(
oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s,
oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s,
oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c
);
p = m * p;
}vec3 foldRotate(vec3 p, float timeOffset) {
for (int i = 0; i < 5; i++) {
p = abs(p); // Mirror fold
rot(p, vec3(0.707, 0.707, 0.0), 0.785);
p -= 0.5; // Translate
}
return p;
}float vignette(vec2 uv) {
uv *= 1.0 - uv.yx;
return pow(uv.x * uv.y * 15.0, 0.25);
}float dither = hash21(fragCoord + iTime) * 0.001;
finalCol += dither;finalCol = pow(finalCol, vec3(0.45)); // ~1/2.2void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
// Generate or compute values
fragColor = vec4(computedColor, 1.0);
}#define BUFFER_A iChannel0
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
vec4 current = texture(BUFFER_A, uv);
vec4 previous = texture(iChannel1, uv); // Self-reference
fragColor = mix(previous, current, 0.1); // Temporal blend
}#define BUFFER_B iChannel1
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord / iResolution.xy;
fragColor = texture(BUFFER_B, uv);
}fragColor = vec4(vec3(distanceField), 1.0); // Show distance
fragColor = vec4(normal * 0.5 + 0.5, 1.0); // Show normals
fragColor = vec4(fract(uv), 0.0, 1.0); // Show UV tiling// Fork of "Original Name" by AuthorName. https://shadertoy.com/view/XxXxXx
// Date: YYYY-MM-DD
// License: Creative Commons (CC BY-NC-SA 4.0) [or other]#define PI 3.1415926535897932384626433832795
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
// 1. Normalize coordinates
vec2 uv = (fragCoord * 2.0 - iResolution.xy) / min(iResolution.x, iResolution.y);
// 2. Compute effect
float d = length(uv) - 0.5; // Circle distance field
vec3 col = vec3(smoothstep(0.01, 0.0, d)); // Sharp edge
// 3. Animate with time
col *= 0.5 + 0.5 * sin(iTime + uv.xyx * 3.0);
// 4. Apply palette
col = palette(col.x, vec3(0.5), vec3(0.5), vec3(1.0), vec3(0.0));
// 5. Post-process
col = pow(col, vec3(0.45)); // Gamma
col *= vignette(fragCoord / iResolution.xy);
// 6. Output
fragColor = vec4(col, 1.0);
}