Components
Loading preview...
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>
);
}Loading preview...