token waste

shadergen.tsx

Shaders

About

a 3d webgl generator

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds shadergen.tsx to your project and fits it to your theme.

Code

How shadergen.tsx is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

import React, { useEffect, useRef, useState, useCallback } from 'react';

const VERTEX_SHADER = `#version 300 es
in vec2 position;
void main() {
  gl_Position = vec4(position, 0.0, 1.0);
}`;

const SHADERS = {
  voronoi: `#version 300 es
precision highp float;
uniform vec2 r;
uniform float t;
uniform float u_scale, u_speed, u_intensity;
out vec4 fragColor;

vec2 hash2(vec2 p) {
  p = vec2(dot(p,vec2(127.1,311.7)), dot(p,vec2(269.5,183.3)));
  return fract(sin(p)*43758.5453);
}

float voronoi(vec2 x) {
  vec2 n = floor(x);
  vec2 f = fract(x);
  float minDist = 8.0;
  for(int j=-1; j<=1; j++) {
    for(int i=-1; i<=1; i++) {
      vec2 g = vec2(float(i),float(j));
      vec2 o = hash2(n + g);
      o = 0.5 + 0.5*sin(t*u_speed + 6.2831*o);
      vec2 r = g + o - f;
      float d = dot(r,r);
      minDist = min(minDist, d);
    }
  }
  return sqrt(minDist);
}

void main() {
  vec2 uv = gl_FragCoord.xy / r;
  vec2 p = uv * u_scale;
  float v = voronoi(p);
  vec3 col = vec3(v) * u_intensity;
  col = mix(vec3(0.1,0.3,0.8), vec3(1.0,0.5,0.2), smoothstep(0.0,1.0,v));
  fragColor = vec4(col, 1.0);
}`,

  simplex: `#version 300 es
precision highp float;
uniform vec2 r;
uniform float t;
uniform float u_scale, u_speed, u_octaves;
out vec4 fragColor;

vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec2 mod289(vec2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; }
vec3 permute(vec3 x) { return mod289(((x*34.0)+1.0)*x); }

float snoise(vec2 v) {
  const vec4 C = vec4(0.211324865405187, 0.366025403784439, -0.577350269189626, 0.024390243902439);
  vec2 i  = floor(v + dot(v, C.yy));
  vec2 x0 = v - i + dot(i, C.xx);
  vec2 i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
  vec4 x12 = x0.xyxy + C.xxzz;
  x12.xy -= i1;
  i = mod289(i);
  vec3 p = permute(permute(i.y + vec3(0.0, i1.y, 1.0)) + i.x + vec3(0.0, i1.x, 1.0));
  vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
  m = m*m; m = m*m;
  vec3 x = 2.0 * fract(p * C.www) - 1.0;
  vec3 h = abs(x) - 0.5;
  vec3 ox = floor(x + 0.5);
  vec3 a0 = x - ox;
  m *= 1.79284291400159 - 0.85373472095314 * (a0*a0 + h*h);
  vec3 g;
  g.x = a0.x * x0.x + h.x * x0.y;
  g.yz = a0.yz * x12.xz + h.yz * x12.yw;
  return 130.0 * dot(m, g);
}

float fbm(vec2 p) {
  float f = 0.0, a = 0.5;
  for(int i=0; i<8; i++) {
    if(float(i) >= u_octaves) break;
    f += a * snoise(p);
    p *= 2.0;
    a *= 0.5;
  }
  return f;
}

void main() {
  vec2 uv = gl_FragCoord.xy / r;
  vec2 p = (uv - 0.5) * u_scale;
  float n = fbm(p + t * u_speed);
  vec3 col = vec3(n * 0.5 + 0.5);
  col = mix(vec3(0.2,0.4,0.8), vec3(1.0,0.9,0.6), col.x);
  fragColor = vec4(col, 1.0);
}`,

  curl: `#version 300 es
precision highp float;
uniform vec2 r;
uniform float t;
uniform float u_scale, u_speed, u_strength;
out vec4 fragColor;

float hash(vec2 p) { return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5); }
float noise(vec2 p) {
  vec2 i = floor(p), f = fract(p);
  f = f*f*(3.0-2.0*f);
  return mix(mix(hash(i),hash(i+vec2(1,0)),f.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x),f.y);
}

vec2 curl(vec2 p) {
  float eps = 0.001;
  float dx = (noise(p+vec2(eps,0.0)) - noise(p-vec2(eps,0.0)))/(2.0*eps);
  float dy = (noise(p+vec2(0.0,eps)) - noise(p-vec2(0.0,eps)))/(2.0*eps);
  return vec2(dy, -dx) * u_strength;
}

void main() {
  vec2 uv = gl_FragCoord.xy / r;
  vec2 p = (uv - 0.5) * u_scale;
  vec2 c = curl(p + t * u_speed);
  float len = length(c);
  vec3 col = vec3(len);
  col = mix(vec3(0.1,0.2,0.4), vec3(0.8,0.4,1.0), smoothstep(0.0,2.0,len));
  fragColor = vec4(col, 1.0);
}`,

  turbulence: `#version 300 es
precision highp float;
uniform vec2 r;
uniform float t;
uniform float u_scale, u_speed, u_power;
out vec4 fragColor;

float hash(vec2 p) { return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5); }
float noise(vec2 p) {
  vec2 i = floor(p), f = fract(p);
  f = f*f*(3.0-2.0*f);
  return mix(mix(hash(i),hash(i+vec2(1,0)),f.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x),f.y);
}

float turbulence(vec2 p) {
  float t = 0.0, f = 1.0;
  for(int i=0; i<6; i++) {
    t += abs(noise(p * f)) / f;
    f *= 2.0;
  }
  return t;
}

void main() {
  vec2 uv = gl_FragCoord.xy / r;
  vec2 p = (uv - 0.5) * u_scale + t * u_speed;
  float turb = turbulence(p) * u_power;
  vec3 col = vec3(turb);
  col = mix(vec3(1.0,0.2,0.0), vec3(1.0,1.0,0.3), smoothstep(0.3,0.7,turb));
  fragColor = vec4(col, 1.0);
}`,

  ridged: `#version 300 es
precision highp float;
uniform vec2 r;
uniform float t;
uniform float u_scale, u_speed, u_sharpness;
out vec4 fragColor;

float hash(vec2 p) { return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5); }
float noise(vec2 p) {
  vec2 i = floor(p), f = fract(p);
  f = f*f*(3.0-2.0*f);
  return mix(mix(hash(i),hash(i+vec2(1,0)),f.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x),f.y);
}

float ridged(vec2 p) {
  float sum = 0.0, amp = 1.0;
  for(int i=0; i<6; i++) {
    float n = noise(p);
    n = 1.0 - abs(n * 2.0 - 1.0);
    n = pow(n, u_sharpness);
    sum += n * amp;
    p *= 2.0;
    amp *= 0.5;
  }
  return sum;
}

void main() {
  vec2 uv = gl_FragCoord.xy / r;
  vec2 p = (uv - 0.5) * u_scale + t * u_speed;
  float r = ridged(p);
  vec3 col = vec3(r);
  col = mix(vec3(0.1,0.15,0.2), vec3(0.9,0.95,1.0), pow(r,0.7));
  fragColor = vec4(col, 1.0);
}`,

  domainwarp: `#version 300 es
precision highp float;
uniform vec2 r;
uniform float t;
uniform float u_scale, u_speed, u_warp;
out vec4 fragColor;

float hash(vec2 p) { return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5); }
float noise(vec2 p) {
  vec2 i = floor(p), f = fract(p);
  f = f*f*(3.0-2.0*f);
  return mix(mix(hash(i),hash(i+vec2(1,0)),f.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x),f.y);
}

float fbm(vec2 p) {
  float v = 0.0, a = 0.5;
  for(int i=0; i<5; i++) {
    v += a * noise(p);
    p *= 2.0;
    a *= 0.5;
  }
  return v;
}

void main() {
  vec2 uv = gl_FragCoord.xy / r;
  vec2 p = (uv - 0.5) * u_scale;
  vec2 q = vec2(fbm(p+t*u_speed), fbm(p+vec2(5.2)));
  vec2 w = vec2(fbm(p+u_warp*q+vec2(1.7,9.2)), fbm(p+u_warp*q+vec2(8.3,2.8)));
  float f = fbm(p+u_warp*w);
  vec3 col = mix(vec3(0.1,0.2,0.5), vec3(0.9,0.5,0.3), f);
  fragColor = vec4(col, 1.0);
}`,

  kaleidoscope: `#version 300 es
precision highp float;
uniform vec2 r;
uniform float t;
uniform float u_segments, u_speed, u_zoom;
out vec4 fragColor;

float hash(vec2 p) { return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5); }
float noise(vec2 p) {
  vec2 i = floor(p), f = fract(p);
  f = f*f*(3.0-2.0*f);
  return mix(mix(hash(i),hash(i+vec2(1,0)),f.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x),f.y);
}

void main() {
  vec2 uv = (gl_FragCoord.xy - r*0.5) / r.y;
  float a = atan(uv.y, uv.x);
  float d = length(uv) * u_zoom;
  float seg = 6.28318 / u_segments;
  a = mod(a, seg) - seg*0.5;
  a = abs(a);
  vec2 p = vec2(cos(a), sin(a)) * d;
  float n = noise(p * 5.0 + t * u_speed);
  vec3 col = mix(vec3(0.8,0.2,0.5), vec3(0.2,0.8,1.0), n);
  fragColor = vec4(col, 1.0);
}`,

  polar: `#version 300 es
precision highp float;
uniform vec2 r;
uniform float t;
uniform float u_arms, u_speed, u_twist;
out vec4 fragColor;

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

void main() {
  vec2 uv = (gl_FragCoord.xy - r*0.5) / r.y;
  float a = atan(uv.y, uv.x) + t * u_speed;
  float d = length(uv);
  float spiral = sin(u_arms * a + d * u_twist) * 0.5 + 0.5;
  vec3 col = mix(vec3(0.2,0.1,0.4), vec3(1.0,0.6,0.8), spiral);
  fragColor = vec4(col, 1.0);
}`,

  sdf: `#version 300 es
precision highp float;
uniform vec2 r;
uniform float t;
uniform float u_morph, u_speed, u_smoothness;
out vec4 fragColor;

float sdCircle(vec2 p, float rad) { return length(p) - rad; }
float sdBox(vec2 p, vec2 b) { vec2 d = abs(p)-b; return length(max(d,0.0)) + min(max(d.x,d.y),0.0); }
float sdHex(vec2 p, float r) {
  const vec3 k = vec3(-0.866025404,0.5,0.577350269);
  p = abs(p);
  p -= 2.0*min(dot(k.xy,p),0.0)*k.xy;
  p -= vec2(clamp(p.x, -k.z*r, k.z*r), r);
  return length(p)*sign(p.y);
}

void main() {
  vec2 uv = (gl_FragCoord.xy - r*0.5) / r.y;
  float d1 = sdCircle(uv, 0.3);
  float d2 = sdBox(uv, vec2(0.25));
  float d3 = sdHex(uv, 0.3);
  float m = sin(t*u_speed) * 0.5 + 0.5;
  float d = mix(mix(d1,d2,m*u_morph), d3, (1.0-m)*u_morph);
  float edge = smoothstep(0.0, u_smoothness, abs(d));
  vec3 col = mix(vec3(0.9,0.3,0.5), vec3(0.1,0.2,0.4), edge);
  fragColor = vec4(col, 1.0);
}`,

  palette: `#version 300 es
precision highp float;
uniform vec2 r;
uniform float t;
uniform float u_freq1, u_freq2, u_freq3;
out vec4 fragColor;

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

void main() {
  vec2 uv = (gl_FragCoord.xy - r*0.5) / r.y;
  float d = length(uv);
  float a = atan(uv.y, uv.x);
  float pattern = sin(d*u_freq1 - t) * cos(a*u_freq2 + t*0.5) + sin(a*u_freq3 - d*5.0);
  vec3 col = palette(pattern, vec3(0.5),vec3(0.5),vec3(1.0,1.0,0.5),vec3(0.0,0.1,0.2));
  fragColor = vec4(col, 1.0);
}`,

  normal: `#version 300 es
precision highp float;
uniform vec2 r;
uniform float t;
uniform float u_scale, u_height, u_speed;
out vec4 fragColor;

float hash(vec2 p) { return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5); }
float noise(vec2 p) {
  vec2 i = floor(p), f = fract(p);
  f = f*f*(3.0-2.0*f);
  return mix(mix(hash(i),hash(i+vec2(1,0)),f.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x),f.y);
}

void main() {
  vec2 uv = gl_FragCoord.xy / r;
  vec2 p = uv * u_scale + t * u_speed;
  float eps = 0.01;
  float h = noise(p);
  float hx = noise(p + vec2(eps,0.0));
  float hy = noise(p + vec2(0.0,eps));
  vec3 normal = normalize(vec3((h-hx)*u_height, (h-hy)*u_height, eps));
  vec3 light = normalize(vec3(1.0,1.0,1.0));
  float diff = max(dot(normal, light), 0.0);
  vec3 col = vec3(diff) * vec3(0.7,0.8,1.0);
  fragColor = vec4(col, 1.0);
}`,

  chromatic: `#version 300 es
precision highp float;
uniform vec2 r;
uniform float t;
uniform float u_offset, u_speed, u_distortion;
out vec4 fragColor;

float hash(vec2 p) { return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5); }
float noise(vec2 p) {
  vec2 i = floor(p), f = fract(p);
  f = f*f*(3.0-2.0*f);
  return mix(mix(hash(i),hash(i+vec2(1,0)),f.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x),f.y);
}

void main() {
  vec2 uv = gl_FragCoord.xy / r;
  vec2 center = uv - 0.5;
  float dist = length(center);
  vec2 offset = normalize(center) * u_offset * dist * u_distortion;
  float r = noise((uv + offset*1.0)*10.0 + t*u_speed);
  float g = noise((uv + offset*0.0)*10.0 + t*u_speed);
  float b = noise((uv - offset*1.0)*10.0 + t*u_speed);
  fragColor = vec4(r,g,b,1.0);
}`,

  reaction: `#version 300 es
precision highp float;
uniform vec2 r;
uniform float t;
uniform float u_feed, u_kill, u_speed;
out vec4 fragColor;

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

void main() {
  vec2 uv = gl_FragCoord.xy / r;
  vec2 p = uv * 20.0;
  float time = t * u_speed;
  float a = hash(floor(p) + floor(time));
  float b = hash(floor(p) + floor(time*1.3));
  float reaction = a*a - b*b;
  reaction = reaction * u_feed - b * u_kill;
  vec3 col = mix(vec3(0.9,0.3,0.4), vec3(0.2,0.8,0.6), smoothstep(-0.5,0.5,reaction));
  fragColor = vec4(col, 1.0);
}`,

  flowfield: `#version 300 es
precision highp float;
uniform vec2 r;
uniform float t;
uniform float u_scale, u_speed, u_density;
out vec4 fragColor;

float hash(vec2 p) { return fract(sin(dot(p,vec2(127.1,311.7)))*43758.5); }
float noise(vec2 p) {
  vec2 i = floor(p), f = fract(p);
  f = f*f*(3.0-2.0*f);
  return mix(mix(hash(i),hash(i+vec2(1,0)),f.x),mix(hash(i+vec2(0,1)),hash(i+vec2(1,1)),f.x),f.y);
}

void main() {
  vec2 uv = gl_FragCoord.xy / r;
  vec2 p = uv * u_scale;
  float angle = noise(p + t*u_speed) * 6.28318;
  vec2 flow = vec2(cos(angle), sin(angle));
  float density = sin(dot(p, flow) * u_density + t) * 0.5 + 0.5;
  vec3 col = mix(vec3(0.1,0.2,0.3), vec3(0.8,0.9,1.0), density);
  fragColor = vec4(col, 1.0);
}`,

  raymarch: `#version 300 es
precision highp float;
uniform vec2 r;
uniform float t;
uniform float u_density, u_speed, u_depth;
out vec4 fragColor;

float hash(vec3 p) { return fract(sin(dot(p,vec3(127.1,311.7,74.7)))*43758.5); }
float noise(vec3 p) {
  vec3 i = floor(p), f = fract(p);
  f = f*f*(3.0-2.0*f);
  return mix(mix(mix(hash(i),hash(i+vec3(1,0,0)),f.x),mix(hash(i+vec3(0,1,0)),hash(i+vec3(1,1,0)),f.x),f.y),
             mix(mix(hash(i+vec3(0,0,1)),hash(i+vec3(1,0,1)),f.x),mix(hash(i+vec3(0,1,1)),hash(i+vec3(1,1,1)),f.x),f.y),f.z);
}

void main() {
  vec2 uv = (gl_FragCoord.xy - r*0.5) / r.y;
  vec3 rd = normalize(vec3(uv, 1.0));
  vec3 ro = vec3(0.0, 0.0, t*u_speed);
  float acc = 0.0;
  for(int i=0; i<32; i++) {
    if(float(i) > u_depth) break;
    vec3 p = ro + rd * float(i) * 0.1;
    acc += noise(p * u_density) * 0.03;
  }
  vec3 col = vec3(acc) * vec3(0.6,0.8,1.0);
  fragColor = vec4(col, 1.0);
}`
};

const SHADER_CONFIGS = {
  voronoi: { scale: 10, speed: 0.5, intensity: 1.5 },
  simplex: { scale: 5, speed: 0.1, octaves: 6 },
  curl: { scale: 5, speed: 0.1, strength: 2 },
  turbulence: { scale: 3, speed: 0.05, power: 1 },
  ridged: { scale: 5, speed: 0.02, sharpness: 2 },
  domainwarp: { scale: 3, speed: 0.1, warp: 4 },
  kaleidoscope: { segments: 8, speed: 0.5, zoom: 2 },
  polar: { arms: 5, speed: 0.5, twist: 10 },
  sdf: { morph: 1, speed: 1, smoothness: 0.02 },
  palette: { freq1: 10, freq2: 8, freq3: 6 },
  normal: { scale: 10, height: 20, speed: 0.1 },
  chromatic: { offset: 0.01, speed: 0.5, distortion: 1 },
  reaction: { feed: 0.055, kill: 0.062, speed: 0.1 },
  flowfield: { scale: 10, speed: 0.2, density: 20 },
  raymarch: { density: 3, speed: 0.5, depth: 32 }
};

export default function ShaderGallery() {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const glRef = useRef<WebGL2RenderingContext | null>(null);
  const programsRef = useRef<Map<string, WebGLProgram>>(new Map());
  const rafRef = useRef<number>();
  const startTimeRef = useRef(Date.now());
  
  const [currentShader, setCurrentShader] = useState('voronoi');
  const [params, setParams] = useState(SHADER_CONFIGS.voronoi);
  const [isPlaying, setIsPlaying] = useState(true);

  const initGL = useCallback(() => {
    const canvas = canvasRef.current;
    if (!canvas) return false;
    
    const gl = canvas.getContext('webgl2', { 
      antialias: true, 
      alpha: false, 
      powerPreference: 'high-performance' 
    });
    
    if (!gl) return false;
    glRef.current = gl;
    
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    gl.viewport(0, 0, canvas.width, canvas.height);
    
    return true;
  }, []);

  const compileShader = useCallback((gl: WebGL2RenderingContext, type: number, source: string) => {
    const shader = gl.createShader(type)!;
    gl.shaderSource(shader, source);
    gl.compileShader(shader);
    return shader;
  }, []);

  const createProgram = useCallback((gl: WebGL2RenderingContext, fragSource: string) => {
    const vs = compileShader(gl, gl.VERTEX_SHADER, VERTEX_SHADER);
    const fs = compileShader(gl, gl.FRAGMENT_SHADER, fragSource);
    
    const program = gl.createProgram()!;
    gl.attachShader(program, vs);
    gl.attachShader(program, fs);
    gl.linkProgram(program);
    
    gl.deleteShader(vs);
    gl.deleteShader(fs);
    
    const vao = gl.createVertexArray();
    const vbo = gl.createBuffer();
    gl.bindVertexArray(vao);
    gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1,1,-1,-1,1,1,1]), gl.STATIC_DRAW);
    
    const posLoc = gl.getAttribLocation(program, 'position');
    gl.enableVertexAttribArray(posLoc);
    gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
    
    return program;
  }, [compileShader]);

  useEffect(() => {
    if (!initGL()) return;
    
    const gl = glRef.current!;
    Object.entries(SHADERS).forEach(([name, source]) => {
      programsRef.current.set(name, createProgram(gl, source));
    });
    
    const handleResize = () => {
      const canvas = canvasRef.current;
      if (!canvas || !gl) return;
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
      gl.viewport(0, 0, canvas.width, canvas.height);
    };
    
    window.addEventListener('resize', handleResize);
    
    const render = () => {
      if (!isPlaying) return;
      
      const canvas = canvasRef.current;
      const program = programsRef.current.get(currentShader);
      if (!canvas || !gl || !program) return;
      
      gl.useProgram(program);
      
      const t = (Date.now() - startTimeRef.current) / 1000;
      const rLoc = gl.getUniformLocation(program, 'r');
      const tLoc = gl.getUniformLocation(program, 't');
      
      gl.uniform2f(rLoc, canvas.width, canvas.height);
      gl.uniform1f(tLoc, t);
      
      Object.entries(params).forEach(([key, value]) => {
        const loc = gl.getUniformLocation(program, `u_${key}`);
        if (loc) gl.uniform1f(loc, value as number);
      });
      
      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
      rafRef.current = requestAnimationFrame(render);
    };
    
    render();
    
    return () => {
      window.removeEventListener('resize', handleResize);
      if (rafRef.current) cancelAnimationFrame(rafRef.current);
      programsRef.current.forEach(p => gl.deleteProgram(p));
      programsRef.current.clear();
    };
  }, [initGL, createProgram, currentShader, params, isPlaying]);

  useEffect(() => {
    if (isPlaying && !rafRef.current) {
      const render = () => {
        const canvas = canvasRef.current;
        const gl = glRef.current;
        const program = programsRef.current.get(currentShader);
        if (!canvas || !gl || !program) return;
        
        gl.useProgram(program);
        const t = (Date.now() - startTimeRef.current) / 1000;
        const rLoc = gl.getUniformLocation(program, 'r');
        const tLoc = gl.getUniformLocation(program, 't');
        
        gl.uniform2f(rLoc, canvas.width, canvas.height);
        gl.uniform1f(tLoc, t);
        
        Object.entries(params).forEach(([key, value]) => {
          const loc = gl.getUniformLocation(program, `u_${key}`);
          if (loc) gl.uniform1f(loc, value as number);
        });
        
        gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
        rafRef.current = requestAnimationFrame(render);
      };
      render();
    } else if (!isPlaying && rafRef.current) {
      cancelAnimationFrame(rafRef.current);
      rafRef.current = undefined;
    }
  }, [isPlaying, currentShader, params]);

  const handleShaderChange = (shader: string) => {
    setCurrentShader(shader);
    setParams(SHADER_CONFIGS[shader as keyof typeof SHADER_CONFIGS]);
  };

  const handleParamChange = (key: string, value: number) => {
    setParams(prev => ({ ...prev, [key]: value }));
  };

  const shaderNames = {
    voronoi: 'Voronoi Noise',
    simplex: 'Simplex Noise',
    curl: 'Curl Noise',
    turbulence: 'Turbulence',
    ridged: 'Ridged Multifractal',
    domainwarp: 'Domain Warping',
    kaleidoscope: 'Kaleidoscopic',
    polar: 'Polar Distortion',
    sdf: 'Signed Distance Fields',
    palette: 'Palette Cycling',
    normal: 'Normal Mapping',
    chromatic: 'Chromatic Aberration',
    reaction: 'Reaction-Diffusion',
    flowfield: 'Flow Fields',
    raymarch: 'Raymarch Volumetrics'
  };

  return (
    <div style={{ position: 'fixed', inset: 0, background: '#000', overflow: 'hidden' }}>
      <canvas ref={canvasRef} style={{ display: 'block', width: '100%', height: '100%' }} />
      
      <div style={{ 
        position: 'absolute', 
        top: 20, 
        left: 20, 
        background: 'rgba(0,0,0,0.85)', 
        padding: '20px', 
        borderRadius: '12px',
        color: '#fff',
        fontFamily: 'monospace',
        maxHeight: 'calc(100vh - 40px)',
        overflowY: 'auto',
        backdropFilter: 'blur(10px)',
        border: '1px solid rgba(255,255,255,0.1)',
        minWidth: '280px'
      }}>
        <h2 style={{ margin: '0 0 16px 0', fontSize: '18px', fontWeight: 700 }}>Shader Gallery</h2>
        
        <button 
          onClick={() => setIsPlaying(!isPlaying)}
          style={{
            width: '100%',
            padding: '10px',
            marginBottom: '16px',
            background: isPlaying ? '#f44336' : '#4CAF50',
            border: 'none',
            borderRadius: '6px',
            color: '#fff',
            cursor: 'pointer',
            fontWeight: 600,
            fontSize: '14px'
          }}
        >
          {isPlaying ? 'Pause' : 'Play'}
        </button>
        
        <div style={{ marginBottom: '20px' }}>
          <label style={{ display: 'block', marginBottom: '8px', fontSize: '12px', opacity: 0.7 }}>
            Select Shader
          </label>
          <select 
            value={currentShader}
            onChange={(e) => handleShaderChange(e.target.value)}
            style={{
              width: '100%',
              padding: '8px',
              background: '#1a1a1a',
              border: '1px solid #333',
              borderRadius: '6px',
              color: '#fff',
              fontSize: '13px',
              cursor: 'pointer'
            }}
          >
            {Object.entries(shaderNames).map(([key, name]) => (
              <option key={key} value={key}>{name}</option>
            ))}
          </select>
        </div>
        
        <div style={{ borderTop: '1px solid #333', paddingTop: '16px' }}>
          <h3 style={{ margin: '0 0 12px 0', fontSize: '14px', opacity: 0.8 }}>Parameters</h3>
          {Object.entries(params).map(([key, value]) => (
            <div key={key} style={{ marginBottom: '16px' }}>
              <label style={{ 
                display: 'flex', 
                justifyContent: 'space-between', 
                marginBottom: '6px',
                fontSize: '12px',
                textTransform: 'capitalize'
              }}>
                <span>{key.replace('_', ' ')}</span>
                <span style={{ opacity: 0.6 }}>{(value as number).toFixed(2)}</span>
              </label>
              <input
                type="range"
                min="0"
                max={key.includes('octaves') || key.includes('depth') ? 16 : key.includes('segments') ? 24 : 20}
                step="0.1"
                value={value as number}
                onChange={(e) => handleParamChange(key, parseFloat(e.target.value))}
                style={{
                  width: '100%',
                  accentColor: '#2196F3'
                }}
              />
            </div>
          ))}
        </div>
        
        <button
          onClick={() => setParams(SHADER_CONFIGS[currentShader as keyof typeof SHADER_CONFIGS])}
          style={{
            width: '100%',
            padding: '8px',
            marginTop: '12px',
            background: '#333',
            border: '1px solid #555',
            borderRadius: '6px',
            color: '#fff',
            cursor: 'pointer',
            fontSize: '12px'
          }}
        >
          Reset Parameters
        </button>
      </div>
    </div>
  );
}

More by token waste

View all
  • copperpatina.tsx

    copperpatina.tsx

More in Shaders

View all
  • Glass Fold

    Glass Fold

    213
  • Neural Noise

    Neural Noise

    718
  • Water Caustic

    Water Caustic

    44
  • Shader Background

    Shader Background

    2K
  • Wave

    Wave

    688
  • Stripe like gradient-shader

    Stripe like gradient-shader

    265
  • Black Hole

    Black Hole

    23
  • Waves shaders Homlu UI

    Waves shaders Homlu UI

    92