diff --git a/README.md b/README.md index 2ee7b0b..ad04416 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,19 @@ # Wall of Entropy — GPU WebGL pack -This is a small GPU-powered WebGL shader version of the "wall of entropy" (lava-lamp wall). It runs in the browser (no build step), using p5.js to create a WebGL canvas and a GLSL fragment shader for the gooey blobs. +This is a small GPU-powered WebGL shader version of the "wall of entropy" (lava-lamp wall). It runs in the browser (no build step), using p5.js to create a WebGL canvas and a GLSL fragment shader for the gooey blobs. Features a 5x4 grid of lava lamps with truly random behavior using the Web Crypto API. Files: - index.html — entry page (contains shader sources and loads sketch.js) -- sketch.js — p5 sketch that sets up the shader and passes uniforms +- sketch.js — p5 sketch that sets up the shader and passes uniforms (including truly random seeds) +- run.bat — Windows batch file to easily run the application - make_zip.ps1 — PowerShell script to make gpu_wall.zip on Windows - make_zip.sh — bash script to make gpu_wall.zip on macOS/Linux How to run locally: -1. Save all files in the same folder. -2. Open index.html in a modern browser (Chrome, Edge, Firefox). For best performance use Chrome/Edge. +1. **Windows (Easy Method)**: Double-click `run.bat` - This will start a local server and open your browser automatically. +2. **Manual Method**: + - Save all files in the same folder. + - Open index.html in a modern browser (Chrome, Edge, Firefox). For best performance use Chrome/Edge. - Optionally serve with a simple static server (recommended for consistent behavior): - Python 3: python -m http.server 8000 - Then visit http://localhost:8000/index.html @@ -20,4 +23,12 @@ How to run locally: Loop duration: - Default loop duration is 12 seconds. To override, add ?loop=20.0 (seconds) to the URL: - index.html?loop=20 \ No newline at end of file + index.html?loop=20 + +Features: +- 5x4 grid of lava lamps (20 lamps total) +- Truly random initialization using Web Crypto API +- High-quality WebGL shader rendering +- Realistic lava lamp physics with organic blob motion +- Vibrant color palettes +- Smooth looping animation \ No newline at end of file diff --git a/index.html b/index.html index 07b5ee1..d297440 100644 --- a/index.html +++ b/index.html @@ -40,6 +40,7 @@ uniform float u_time; uniform float u_loop; // loop duration in seconds uniform float u_pixel_ratio; + uniform float u_random_seeds[20]; // truly random seeds for each lamp (MUST match COLS*ROWS = 5*4 = 20) // Hash / basic noise (IQ style) float hash(vec2 p) { @@ -96,6 +97,8 @@ // for each cell produce a soft lamp for (int y = 0; y < ROWS; y++) { for (int x = 0; x < COLS; x++) { + int lampIndex = y * COLS + x; + // compute cell center in uv-space vec2 cellCenter = vec2( -1.0 + (float(x) + 0.5) * cellW, @@ -105,8 +108,8 @@ // small local coordinates vec2 lp = (uv - cellCenter) / 0.9; - // seed per-lamp (deterministic) - float seed = float(x*31 + y*97); + // Use truly random seed for this lamp + float seed = u_random_seeds[lampIndex] * 1000.0; // animate multiple blobs that rotate/oscillate; make motion depend on timeVec for loopability float nblobs = 4.0 + mod(seed, 3.0); @@ -116,29 +119,33 @@ float phase = seed*0.19 + fi * 2.3; // angle for orbiting blob: combines global looping angle + seeded offsets float ang = angle* (0.8 + 0.2*sin(seed*0.7+fi)) + phase; - float r = 0.14 + 0.10 * sin(seed*1.9 + fi*0.5); + float r = 0.16 + 0.12 * sin(seed*1.9 + fi*0.5); vec2 center = vec2(cos(ang+fi)*r, sin(ang*0.9 - fi*0.6)*r) * (0.9 + 0.2*noise(vec2(seed,fi))); // jitter with fbm for organic motion (but keep periodic by using timeVec in sample) vec2 jitterInput = vec2(seed*13.1 + fi*7.3, dot(timeVec, vec2(1.0, -1.0))*0.5); - vec2 jitter = vec2(fbm(jitterInput + vec2(0.0, seed*0.01)), fbm(jitterInput + vec2(1.0, seed*0.01))) * 0.03; + vec2 jitter = vec2(fbm(jitterInput + vec2(0.0, seed*0.01)), fbm(jitterInput + vec2(1.0, seed*0.01))) * 0.04; vec2 cpos = center + jitter; float dist = length(lp - cpos); - // soft circular blob with a little noise modulation - float blob = smoothstep(0.45, 0.0, dist + 0.12*fbm((lp - cpos)*3.0 + vec2(seed,fi))); - local += blob * (0.6 + 0.4*sin(seed*1.3 + fi)); + // Enhanced soft circular blob with more organic noise modulation for realism + float blobSize = 0.25 + 0.08 * sin(seed*1.1 + fi*0.7); + float noiseAmt = 0.15*fbm((lp - cpos)*4.0 + vec2(seed,fi) + timeVec*0.3); + float blob = smoothstep(blobSize + 0.1, 0.0, dist + noiseAmt); + // Add glow around blobs for realistic lava lamp effect + float glow = smoothstep(blobSize + 0.25, blobSize, dist) * 0.3; + local += (blob + glow) * (0.7 + 0.3*sin(seed*1.3 + fi)); } - local = clamp(local, 0.0, 1.5); + local = clamp(local, 0.0, 2.0); - // color per-lamp from palette seeds - vec3 a = vec3(0.8, 0.2, 0.12) * (0.6 + 0.4*sin(seed*0.3)); - vec3 b = vec3(0.2, 0.7, 0.9) * (0.4 + 0.6*cos(seed*0.5)); + // Enhanced color per-lamp from palette seeds for more vibrant, realistic lava lamp colors + vec3 a = vec3(0.9, 0.3, 0.15) * (0.7 + 0.3*sin(seed*0.3)); + vec3 b = vec3(0.3, 0.8, 1.0) * (0.5 + 0.5*cos(seed*0.5)); vec3 c = vec3(1.0, 1.0, 1.0); vec3 d = vec3(0.0, 0.33, 0.67); float palt = fract(seed*0.13 + 0.2 * sin(seed*0.7)); vec3 lampColor = mix(a, b, smoothstep(0.0, 1.0, palt)); - // tint variation - lampColor *= 0.6 + 0.8 * smoothstep(0.0, 1.0, local); + // Enhanced tint variation for depth + lampColor *= 0.7 + 0.9 * smoothstep(0.0, 1.0, local); // accumulate additive glow color += lampColor * local * 0.9; diff --git a/run.bat b/run.bat new file mode 100644 index 0000000..32948bc --- /dev/null +++ b/run.bat @@ -0,0 +1,30 @@ +@echo off +REM Wall of Entropy - 5x4 Lava Lamp Wall Runner +REM This batch file starts a simple HTTP server and opens the lava lamp wall in your default browser + +echo ======================================== +echo Wall of Entropy - 5x4 Lava Lamp Wall +echo ======================================== +echo. +echo Starting local web server... +echo. + +REM Check if Python is available +where python >nul 2>nul +if %errorlevel% == 0 ( + echo Using Python to start server on http://localhost:8000 + echo. + echo Press Ctrl+C to stop the server when done. + echo. + timeout /t 2 >nul + start http://localhost:8000/index.html + python -m http.server 8000 +) else ( + echo ERROR: Python is not installed or not in PATH. + echo. + echo Please install Python 3 from https://www.python.org/ + echo OR simply open index.html directly in your browser. + echo. + pause + exit /b 1 +) diff --git a/sketch.js b/sketch.js index 3ed41ab..431f158 100644 --- a/sketch.js +++ b/sketch.js @@ -2,12 +2,29 @@ let theShader; let loopDuration = 12.0; // seconds (changeable) let paused = false; +let randomSeeds = []; // truly random seeds for each lamp function preload() { // grab shader source from DOM const vert = document.getElementById('vert').textContent; const frag = document.getElementById('frag').textContent; theShader = new p5.Shader(this._renderer, vert, frag); + + // Generate truly random seeds using crypto API + const numLamps = 5 * 4; // 5x4 grid + randomSeeds = new Array(numLamps); + if (window.crypto && window.crypto.getRandomValues) { + const randomBytes = new Uint32Array(numLamps); + window.crypto.getRandomValues(randomBytes); + for (let i = 0; i < numLamps; i++) { + randomSeeds[i] = randomBytes[i] / (0xFFFFFFFF + 1); // normalize to [0,1) + } + } else { + // Fallback to Math.random if crypto not available + for (let i = 0; i < numLamps; i++) { + randomSeeds[i] = Math.random(); + } + } } function setup() { @@ -37,6 +54,7 @@ function draw() { theShader.setUniform('u_loop', loopDuration); theShader.setUniform('u_resolution', [width, height]); theShader.setUniform('u_pixel_ratio', window.devicePixelRatio || 1.0); + theShader.setUniform('u_random_seeds', randomSeeds); rect(-width/2, -height/2, width, height); }