diff --git a/index.html b/index.html index fd6ef10..e34ae86 100644 --- a/index.html +++ b/index.html @@ -795,6 +795,12 @@ RGB Split HSV Rainbow + Thermal + Neon + Cyberpunk + Vaporwave + Dithered + Palette @@ -853,6 +859,12 @@ RGB Split HSV Rainbow + Thermal + Neon + Cyberpunk + Vaporwave + Dithered + Palette diff --git a/src/ShaderWorker.ts b/src/ShaderWorker.ts index f6a0e7f..33d820f 100644 --- a/src/ShaderWorker.ts +++ b/src/ShaderWorker.ts @@ -494,6 +494,24 @@ class ShaderWorker { case 'rainbow': return this.rainbowColor(absValue); + case 'thermal': + return this.thermalColor(absValue); + + case 'neon': + return this.neonColor(absValue); + + case 'cyberpunk': + return this.cyberpunkColor(absValue); + + case 'vaporwave': + return this.vaporwaveColor(absValue); + + case 'dithered': + return this.ditheredColor(absValue); + + case 'palette': + return this.paletteColor(absValue); + default: return [absValue, absValue, absValue]; } @@ -545,6 +563,81 @@ class ShaderWorker { } } + private thermalColor(value: number): [number, number, number] { + const t = value / 255.0; + if (t < 0.25) { + return [0, 0, Math.round(t * 4 * 255)]; + } else if (t < 0.5) { + return [0, Math.round((t - 0.25) * 4 * 255), 255]; + } else if (t < 0.75) { + return [Math.round((t - 0.5) * 4 * 255), 255, Math.round((0.75 - t) * 4 * 255)]; + } else { + return [255, 255, Math.round((t - 0.75) * 4 * 255)]; + } + } + + private neonColor(value: number): [number, number, number] { + const t = value / 255.0; + const intensity = Math.pow(Math.sin(t * Math.PI), 2); + const glow = Math.pow(intensity, 0.5); + return [ + Math.round(glow * 255), + Math.round(intensity * 255), + Math.round(Math.pow(intensity, 2) * 255) + ]; + } + + private cyberpunkColor(value: number): [number, number, number] { + const t = value / 255.0; + const phase = (t * 3) % 1; + if (phase < 0.33) { + return [Math.round(255 * (1 - phase * 3)), 0, Math.round(255 * phase * 3)]; + } else if (phase < 0.67) { + const p = (phase - 0.33) * 3; + return [0, Math.round(255 * p), Math.round(255 * (1 - p))]; + } else { + const p = (phase - 0.67) * 3; + return [Math.round(255 * p), Math.round(255 * (1 - p)), 255]; + } + } + + private vaporwaveColor(value: number): [number, number, number] { + const t = value / 255.0; + const pink = Math.sin(t * Math.PI); + const purple = Math.sin(t * Math.PI * 0.7 + Math.PI / 3); + const blue = Math.sin(t * Math.PI * 0.5 + Math.PI / 2); + return [ + Math.round(255 * (0.8 + 0.2 * pink)), + Math.round(255 * (0.3 + 0.7 * purple)), + Math.round(255 * (0.6 + 0.4 * blue)) + ]; + } + + private ditheredColor(value: number): [number, number, number] { + const levels = 4; + const step = 255 / (levels - 1); + const quantized = Math.round(value / step) * step; + const error = value - quantized; + const dither = (Math.random() - 0.5) * 32; + const final = Math.max(0, Math.min(255, quantized + error + dither)); + return [final, final, final]; + } + + private paletteColor(value: number): [number, number, number] { + const palette = [ + [0, 0, 0], + [87, 29, 149], + [191, 82, 177], + [249, 162, 162], + [255, 241, 165], + [134, 227, 206], + [29, 161, 242], + [255, 255, 255] + ]; + const index = Math.floor((value / 255.0) * (palette.length - 1)); + return palette[index] as [number, number, number]; + } + private sanitizeCode(code: string): string { // Strict whitelist approach - extended to include new interaction variables // Variables: x, y, t, i, mouseX, mouseY, mousePressed, mouseVX, mouseVY, mouseClickTime, touchCount, touch0X, touch0Y, touch1X, touch1Y, pinchScale, pinchRotation, accelX, accelY, accelZ, gyroX, gyroY, gyroZ