bfollington/terma4 files

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.

Specification
Skill ID
bfollington/terma/shadertoy
Publisher
bfollington
Repository
terma
Installs
0
Files
4
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

bfollington/terma/shadertoyInstalls these files
  • SKILL.md
  • references/common-patterns.md
  • references/example-compact-shader.glsl
  • references/glsl-reference.md

What this skill tells the agent

Shadertoy Shader Development

Overview

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.

When to Use This Skill

Activate this skill when:

  • Writing or editing .glsl shader files
  • Creating procedural graphics, generative art, or visual effects
  • Working with Shadertoy.com projects or WebGL fragment shaders
  • Implementing ray marching, distance fields, or procedural textures
  • Debugging shader code or optimizing shader performance
  • Need GLSL ES syntax reference or Shadertoy input variables

Core Concepts

Shader Entry Point

Every Shadertoy shader implements the mainImage function:

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
    // fragCoord: pixel coordinates (0 to iResolution.xy)
    // fragColor: output color (RGBA, typically alpha = 1.0)

    vec2 uv = fragCoord / iResolution.xy;
    fragColor = vec4(uv, 0.0, 1.0);
}

Shadertoy Built-in Inputs

Always available in shaders:

TypeNameDescription
vec3iResolutionViewport resolution (x, y, aspect ratio)
floatiTimeCurrent time in seconds (primary animation driver)
floatiTimeDeltaTime to render one frame
intiFrameCurrent frame number
vec4iMouseMouse: xy = current position, zw = click position
sampler2DiChannel0-iChannel3Input textures/buffers
vec3iChannelResolution[4]Resolution of each input channel
vec4iDateYear, month, day, time in seconds (.xyzw)

Coordinate System Setup

Standard patterns for normalizing coordinates:

// 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;

Common Shader Patterns

1. Procedural Color Palettes

Use Inigo Quilez's cosine palette for smooth color gradients:

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
);

2. Hash Functions (Pseudo-Random)

Simple 2D hash for noise and randomness:

float hash21(vec2 p) {
    p = fract(p * vec2(234.34, 435.345));
    p += dot(p, p + 34.23);
    return fract(p.x * p.y);
}

3. Ray Marching

Standard pattern for 3D rendering via sphere tracing:

// 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
}

4. Rotations

2D rotation:

mat2 rot2d(float a) {
    float c = cos(a), s = sin(a);
    return mat2(c, -s, s, c);
}
// Usage: p.xy *= rot2d(iTime);

3D axis-angle rotation (modifies in-place):

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;
}

5. Domain Repetition and Folding

Create fractal-like structures:

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;
}

6. Post-Processing

Vignette:

float vignette(vec2 uv) {
    uv *= 1.0 - uv.yx;
    return pow(uv.x * uv.y * 15.0, 0.25);
}

Film grain/dithering (reduces banding):

float dither = hash21(fragCoord + iTime) * 0.001;
finalCol += dither;

Gamma correction:

finalCol = pow(finalCol, vec3(0.45));  // ~1/2.2

Multi-Pass Rendering

For complex effects requiring temporal feedback or multiple rendering stages:

Buffer A (Computation):

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord / iResolution.xy;
    // Generate or compute values
    fragColor = vec4(computedColor, 1.0);
}

Buffer B (Feedback/Blending):

#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
}

Main (Final Output):

#define BUFFER_B iChannel1
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord / iResolution.xy;
    fragColor = texture(BUFFER_B, uv);
}

Critical GLSL ES Rules

ALWAYS follow these rules to avoid compilation errors:

  1. NO `f` suffix: Use 1.0 NOT 1.0f
  2. NO `saturate()`: Use clamp(x, 0.0, 1.0) instead
  3. Protect pow/sqrt: Wrap arguments: pow(max(x, 0.0), p), sqrt(abs(x))
  4. Avoid division by zero: Check denominators or add epsilon
  5. Initialize variables: Don't assume default values
  6. Avoid name conflicts: Don't name functions like variables
  7. NO interactive commands: Avoid find, grep - use Glob/Grep tools instead

Workflow Guide

Creating a New Shader

  1. Set up coordinate system - Choose appropriate UV normalization
  2. Define core effect - Implement main visual algorithm
  3. Add animation - Use iTime for temporal variation
  4. Apply color palette - Use cosine palette or custom scheme
  5. Add post-processing - Vignette, dither, gamma correction
  6. Optimize - Reduce iterations, use early exits, minimize branches

Common Tasks

Visualizing complex numbers:

  • Use the complex math functions in references/common-patterns.md
  • Plot with cx_log(), cx_pow(), or polynomial evaluation
  • Map complex results to color via palette

Ray marching 3D scenes:

  • Define distance field in map() function
  • Set up camera (ray origin ro, ray direction rd)
  • March using standard loop pattern
  • Calculate normals with tetrahedron method
  • Apply lighting and material properties

Creating noise/organic effects:

  • Use hash21() for random values
  • Implement fbm() (fractional Brownian motion) for natural variation
  • Combine with sin()/cos() for structured patterns
  • Apply domain warping for organic distortion

Multi-layer composition:

  • Render multiple passes with different parameters
  • Blend layers using mix() or custom blend modes
  • Add interference patterns by comparing layer differences
  • Use smoothstep() for soft transitions

Debugging Strategies

Visualize intermediate values:

Use this skill in a project

Open any RiverX project, search the Skills panel for bfollington/terma/shadertoy, and install it in one click.

Use this skill