WebGL Shader Editor - Run GLSL Locally

Write a WebGL2 fragment shader and use it as ToyCode's persistent site background. Explore GLSL with live animation, mouse input, and compiler diagnostics.

Features

  • Run GLSL ES 3.00 fragment shaders in WebGL2
  • Built-in time, resolution, mouse, and frame uniforms
  • Shader compiler diagnostics in the integrated terminal
  • Persistent site-wide rendering in an isolated worker
  • Share shaders with compressed URLs
#version 300 es
precision highp float;

uniform vec2 u_resolution;
uniform float u_time;
uniform vec2 u_mouse;
uniform int u_frame;

out vec4 fragColor;

vec3 palette(float phase) {
    return 0.55 + 0.45 * cos(6.28318 * (phase + vec3(0.00, 0.18, 0.36)));
}

float hash(vec2 p) {
    return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
}

void main() {
    vec2 uv = gl_FragCoord.xy / u_resolution;
    vec2 p = uv - 0.5;
    p.x *= u_resolution.x / u_resolution.y;
    float t = u_time * 0.45;

    vec3 color = vec3(0.004, 0.006, 0.025);
    color += 0.045 * palette(uv.x * 0.2 + t * 0.025) * exp(-1.4 * length(p));

    for (int i = 0; i < 4; i++) {
        float fi = float(i);
        float offset = (fi - 1.5) * 0.19;
        float wave = 0.14 * sin(p.x * 3.4 + t + fi * 1.7);
        wave += 0.045 * sin(p.x * 8.0 - t * 1.35 + fi);
        float distanceToRibbon = abs(p.y - offset - wave);

        float core = 0.0025 / (distanceToRibbon + 0.006);
        float aura = 0.06 * exp(-distanceToRibbon * 11.0);
        color += (core + aura) * palette(fi * 0.19 + t * 0.018);
    }

    vec2 mouse = u_mouse / u_resolution - 0.5;
    mouse.x *= u_resolution.x / u_resolution.y;
    float mouseBloom = exp(-45.0 * dot(p - mouse, p - mouse));
    color += mouseBloom * vec3(0.015, 0.06, 0.11);

    color += (hash(gl_FragCoord.xy + float(u_frame)) - 0.5) * 0.01;
    color *= 1.0 - 0.55 * dot(uv - 0.5, uv - 0.5);
    color = 1.0 - exp(-color);

    fragColor = vec4(color, 1.0);
}