shader.work|Guides
GuidesCommunityStudio
Log in

templates

These templates are built from the project examples so you can copy, paste, and adapt them in your own sets. Open Studio, create a shader item, then paste any fragment/vertex pair below.

Basic Fragment Starter

A clean full-screen fragment baseline you can customize quickly.

Fragment shaderexample.frag
uniform float iTime;
uniform vec2 iResolution;
uniform float uGlow;
uniform float uInvert;

varying vec2 vUv;

// Rings + glow; uGlow scales the halo (0–10), uInvert flips colors.
void main() {
  vec2 uv = (vUv - 0.5) * 2.0;
  uv.x *= iResolution.x / iResolution.y;

  float r = length(uv);
  float a = atan(uv.y, uv.x);

  float rings = 0.5 + 0.5 * sin(14.0 * r - iTime * 2.0);
  float swirl = 0.5 + 0.5 * sin(a * 3.0 + iTime);
  float glow = exp(-3.0 * r) * (uGlow / 2.5);

  vec3 col = vec3(rings * swirl) * vec3(0.1, 1.0, 0.6) + glow * vec3(0.2, 0.8, 1.0);
  if (uInvert > 0.5) {
    col = vec3(1.0) - col;
  }
  gl_FragColor = vec4(col, 1.0);
}

Wave Pair (Fragment + Vertex)

A matched pair for motion-heavy visuals with explicit vertex control.

Fragment shaderwave.frag
uniform float iTime;
uniform vec2 iResolution;

varying vec2 vUv;

void main() {
  vec2 uv = (vUv - 0.5) * 2.0;
  uv.x *= iResolution.x / iResolution.y;

  float r = length(uv);
  float a = atan(uv.y, uv.x);

  float rings = 0.5 + 0.5 * sin(16.0 * r - iTime * 2.2);
  float swirl = 0.5 + 0.5 * sin(a * 4.0 + iTime * 0.9);
  float glow = exp(-3.2 * r);

  vec3 col = vec3(rings * swirl) * vec3(0.85, 0.15, 0.55) + glow * vec3(0.25, 0.95, 0.75);
  gl_FragColor = vec4(col, 1.0);
}
Vertex shaderwave.vert
// Optional vertex shader: must declare `varying vec2 vUv` if the fragment uses it.
// `iTime` is provided by the studio preview (same uniforms as fragment).
varying vec2 vUv;
uniform float iTime;

void main() {
  vec2 u = uv;
  u += 0.04 * vec2(sin(u.y * 12.0 + iTime * 1.7), cos(u.x * 11.0 + iTime * 1.3));
  vUv = u;
  gl_Position = vec4(position, 1.0);
}

RGB Trails

Color-trail style look useful for reactive, layered live sets.

Fragment shaderrgb-trails-3.frag
uniform float iTime;
uniform vec2 iResolution;

uniform float uRWeight;
uniform float uGWeight;
uniform float uBWeight;
uniform float uAWeight;
uniform float uTrailTime;

varying vec2 vUv;

vec4 sourceColor(vec2 uv, float t) {
  vec2 p = (uv - 0.5) * 2.0;
  p.x *= iResolution.x / max(iResolution.y, 1.0);

  float r = length(p);
  float a = atan(p.y, p.x);

  float wave = 0.5 + 0.5 * sin(10.0 * r - t * 2.4 + sin(a * 3.0 + t * 0.8));
  float swirl = 0.5 + 0.5 * sin(a * 5.0 - t * 1.3 + r * 6.0);
  float pulse = 0.5 + 0.5 * sin(t * 1.7 + r * 18.0);

  vec3 col = vec3(
    0.35 + 0.65 * wave,
    0.25 + 0.75 * swirl,
    0.2 + 0.8 * pulse
  );
  col *= 0.35 + 0.65 * exp(-1.4 * r);

  return vec4(col, 1.0);
}

void main() {
  float trail = max(uTrailTime, 0.0);

  vec4 fresh = sourceColor(vUv, iTime);
  vec4 stale = vec4(
    sourceColor(vUv, iTime - trail * 1.0).r,
    sourceColor(vUv, iTime - trail * 1.3).g,
    sourceColor(vUv, iTime - trail * 1.7).b,
    sourceColor(vUv, iTime - trail * 2.1).a
  );

  gl_FragColor = vec4(
    mix(fresh.r, stale.r, clamp(uRWeight, 0.0, 1.0)),
    mix(fresh.g, stale.g, clamp(uGWeight, 0.0, 1.0)),
    mix(fresh.b, stale.b, clamp(uBWeight, 0.0, 1.0)),
    mix(fresh.a, stale.a, clamp(uAWeight, 0.0, 1.0))
  );
}
Vertex shaderrgb-trails-3.vert
varying vec2 vUv;
uniform float iTime;

void vv_vertShaderInit() {
  vec2 st = uv * 2.0 - 1.0;
  float r2 = dot(st, st);

  // Keep full-screen background where the sphere does not exist.
  if (r2 > 1.0) {
    vUv = uv;
    gl_Position = vec4(position, 1.0);
    return;
  }

  // Map disk -> sphere (upper hemisphere), then rotate over time.
  float z = sqrt(max(0.0, 1.0 - r2));
  vec3 p = vec3(st.x, st.y, z);

  float ay = iTime * 0.8;
  float ax = iTime * 0.45;
  mat3 rotY = mat3(
    cos(ay), 0.0, sin(ay),
    0.0,     1.0, 0.0,
   -sin(ay), 0.0, cos(ay)
  );
  mat3 rotX = mat3(
    1.0, 0.0,      0.0,
    0.0, cos(ax), -sin(ax),
    0.0, sin(ax),  cos(ax)
  );
  p = rotY * rotX * p;

  float perspective = 1.35 + p.z;
  vec2 projected = p.xy / perspective;

  // Reproject sphere coordinates to keep fragment animation coherent.
  vUv = projected * 0.5 + 0.5;
  gl_Position = vec4(projected, 0.0, 1.0);
}

void main() {
  vv_vertShaderInit();
}

Crystal Pearl

A textured style template for softer crystalline/polished moods.

Fragment shadercrystal-pearl.frag
uniform float iTime;
uniform vec2 iResolution;

uniform float uRotSpeed;
uniform float uDispAmp;
uniform float uEdgeWidth;
uniform float uPearlMix;
uniform float uGrain;

varying vec2 vUv;

const float PI = 3.14159265;

mat2 rot(float a) {
  float c = cos(a), s = sin(a);
  return mat2(c, s, -s, c);
}

float map(vec3 p) {
  p.xz *= rot(p.y + iTime * uRotSpeed);
  float displacement =
      sin(p.x * 2.0 + iTime) * sin(p.y * 3.0) * sin(p.z * 2.0) * uDispAmp;
  return length(p) - 1.0 + displacement;
}

vec3 calcNormal(vec3 p) {
  vec2 e = vec2(0.0015, 0.0);
  float dx = map(p + vec3(e.x, e.y, e.y)) - map(p - vec3(e.x, e.y, e.y));
  float dy = map(p + vec3(e.y, e.x, e.y)) - map(p - vec3(e.y, e.x, e.y));
  float dz = map(p + vec3(e.y, e.y, e.x)) - map(p - vec3(e.y, e.y, e.x));
  return normalize(vec3(dx, dy, dz));
}

float raymarch(vec3 ro, vec3 rd, out vec3 pos, out float edge) {
  float t = 0.0;
  edge = 0.0;
  pos = ro;
  for (int i = 0; i < 64; i++) {
    pos = ro + rd * t;
    float d = map(pos);
    edge = 1.0 - smoothstep(0.0, max(0.0005, uEdgeWidth), d);
    if (d < 0.001 || t > 8.0)
      break;
    t += clamp(d * 0.75, 0.015, 0.2);
  }
  return t;
}

vec3 cosinePalette(float t, vec3 a, vec3 b, vec3 c, vec3 d) {
  return a + b * cos(6.28318 * (c * t + d));
}

void main() {
  vec2 fragCoord = vUv * iResolution;
  vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
  vec3 bg = vec3(0.95, 0.95, 0.97);

  vec3 ro = vec3(0.0, 0.0, -3.0);
  vec3 rd = normalize(vec3(uv, 1.2));

  vec3 p;
  float edge;
  float t = raymarch(ro, rd, p, edge);

  vec3 col = bg;

  if (t < 8.0) {
    vec3 N = calcNormal(p);

    vec3 lightDir = normalize(vec3(-0.15, 0.95, 0.25));
    float fresnel = pow(1.0 - max(dot(N, -rd), 0.0), 3.0);

    vec3 baseColor = vec3(1.0);
    vec3 irid =
        0.5 + 0.5 * cos(6.28318 *
                        (vec3(0.0, 0.33, 0.67) + fresnel + p.y * 0.5));

    float diff = 0.5 + 0.5 * max(dot(N, lightDir), 0.0);
    vec3 spec =
        pow(max(dot(reflect(rd, N), lightDir), 0.0), 32.0) * vec3(1.0);

    float band1 = sin(p.y * 4.5 + p.x * 2.0 - iTime * 0.7);
    float band2 = sin((p.y + p.z) * 6.0 + p.x * 1.4 + iTime * 0.5);
    float flow = 0.5 + 0.5 * (0.6 * band1 + 0.4 * band2);
    vec3 pearlTint = cosinePalette(
        flow + fresnel * 0.35, vec3(0.96), vec3(0.05, 0.06, 0.08), vec3(1.0),
        vec3(0.15, 0.45, 0.75));

    col = mix(baseColor, irid, fresnel * 0.8);
    col *= mix(vec3(0.97, 0.98, 1.0), pearlTint, 0.35 * clamp(uPearlMix, 0.0, 1.0));
    col *= mix(0.92, 1.08, diff);
    col += spec * 1.25;
    col += fresnel * fresnel * irid * 0.12;

    float rim = pow(1.0 - max(dot(N, -rd), 0.0), 2.5);
    col += vec3(0.9, 0.95, 1.0) * rim * 0.08;

    col = mix(bg, col, edge);

    float halo = exp(-22.0 * abs(map(p + N * 0.02)));
    col += irid * fresnel * halo * 0.05;
  }

  float vignette = 1.0 - dot(uv, uv) * 0.12;
  col *= vignette;

  float grain = fract(sin(dot(fragCoord + iTime * 37.0, vec2(12.9898, 78.233))) *
                      43758.5453);
  col += (grain - 0.5) * uGrain;

  col = clamp(col, 0.0, 1.0);
  col = pow(col, vec3(0.95));

  gl_FragColor = vec4(col, 1.0);
}

Multi-Window

Template foundation for synchronized multi-display output.

Fragment shadermulti-window.frag
// Multi-window shaders can use `uScreenId` (1 = left, 2 = right, ...).
// Some render paths inject it automatically, but declaring it keeps the shader portable.
uniform int uScreenId;
uniform float iTime;
uniform vec2 iResolution;

uniform float uColorR;
uniform float uColorG;
uniform float uColorB;
uniform float uStripeR;
uniform float uStripeG;
uniform float uStripeB;
uniform float uMoveSpeed;
uniform float uShapeMorph; // 0.0 = circle, 1.0 = triangle

float sdEquilateralTriangle(vec2 p) {
    const float k = 1.7320508;
    p.x = abs(p.x) - 1.0;
    p.y = p.y + 0.57735027;
    if (p.x + k * p.y > 0.0) {
        p = vec2(p.x - k * p.y, -k * p.x - p.y) / 2.0;
    }
    p.x -= clamp(p.x, -2.0, 0.0);
    return -length(p) * sign(p.y);
}

void main() {
    // Normalize pixel coordinates (from 0 to 1) for the LOCAL screen
    vec2 uv = gl_FragCoord.xy / iResolution.xy;
    
    // ---- MULTI-SCREEN COORDINATE SLICING ----
    // Let's assume we have 2 screens side-by-side. 
    // We want to create one massive global UV space that spans both.
    vec2 globalUv = uv;
    
    // If this is screen 2 (the right screen), shift its X coordinate over by 1
    if (uScreenId == 2) {
        globalUv.x += 1.0;
    }
    
    // Scale the X coordinate down by the number of total screens (2) 
    // so the total width spans from 0.0 to 1.0 globally.
    globalUv.x /= 2.0;
    
    // To make a perfect circle instead of an oval, we MUST correct for the aspect ratio!
    // The global aspect ratio is (2 * screenWidth) / screenHeight
    float globalAspect = (2.0 * iResolution.x) / iResolution.y;
    
    // Shift coordinates so (0,0) is the center of the global screens
    globalUv -= 0.5;
    
    // Multiply X by aspect ratio so X and Y use the exact same physical scale
    globalUv.x *= globalAspect;
    
    // Morph between a circle and an equilateral triangle in the shared global space.
    float circleDist = length(globalUv) - 0.55;
    float triangleDist = sdEquilateralTriangle(globalUv / 0.62) * 0.62;
    float shapeDist = mix(circleDist, triangleDist, clamp(uShapeMorph, 0.0, 1.0));
    // Add pulsing rings based on the morphed shape distance.
    float ring = sin(shapeDist * 50.0 - uMoveSpeed);
    ring = smoothstep(0.0, 0.1, ring);
    
    vec3 baseColor = vec3(uColorR, uColorG, uColorB);
    vec3 stripeColor = vec3(uStripeR, uStripeG, uStripeB);
    vec3 color = baseColor;
    
    color += stripeColor * ring;
    
    gl_FragColor = vec4(color, 1.0);
}

Back to Guides