A real-time generative shader experience that transforms your browser into an infinite, breathing abstract landscape. Built entirely with WebGL2, the project renders complex procedural forms directly on the GPU through fragment shader computations, creating saturated dreamlike patterns that evolve continuously across the screen.
The experience maps screen-space coordinates into a logarithmic-polar domain, then applies a series of mathematical transformations including folding, distortion, and time-based rotations. The result is a looping visual field where geometric patterns ripple and breathe, their colors derived from procedural hue functions and luminance-based tone mapping.
Everything happens in real-time on your graphics card. No pre-rendered assets, no image files. Just mathematics and light.
At the core of the rendering pipeline sits a coordinate transformation that converts Cartesian screen positions into logarithmic-polar space. This transformation allows the shader to create infinite recursive patterns from finite screen coordinates.
float d = length(uv) / MN;
uv = vec2(log(d), 0.205 + atan(uv.x, uv.y)) * 8.0 / PI;The logarithm of the distance creates a radial scaling effect where patterns appear to zoom infinitely inward or outward. The arctangent component wraps angular information around this radial axis, establishing the rotational symmetry you see in the forms.
Time drives multiple transformation layers. A rotation matrix modulates with both sine and exponential functions:
uv *= mat2(cos(sin(T*0.2) - vec4(0,11,33,0))) * exp(cos(T)*0.321) * (0.7 + 0.5*cos(T*0.5));This creates the breathing, pulsing quality as geometric forms expand and contract. The multiplication by different phase-shifted cosine values ensures the scaling never settles into a simple periodic loop.
Color generation uses a custom hue function that maps scalar values to RGB through phase-shifted sine waves:
#define hue(a) (0.5 + 0.5*sin(PI*(a) + vec3(1,2,3)))This produces saturated colors across the spectrum without requiring lookup tables or color space conversions. The final color passes through a hyperbolic tangent function for tone mapping, compressing the dynamic range while preserving the vivid saturation.
Fragment shaders process each pixel independently, enabling parallel computation of complex visual effects
Press Ctrl+L to toggle the built-in GLSL editor. The interface provides syntax-aware editing with auto-indentation, tab handling, and real-time error reporting. When you modify the shader source, changes compile and deploy to the GPU with debounced hot-reload, letting you experiment with the mathematics while the experience runs.
The editor tracks cursor position to maintain proper scroll state even as you insert line breaks or indentation. Error messages display with line-number precision, and a visual indicator points to the problematic line in your source.
Shader state persists to localStorage, so your modifications survive page reloads. This makes it practical to develop and refine shaders entirely within the browser environment.
You can reshape the entire visual character by modifying key parameters in the fragment shader. Here are some starting points:
Time scaling affects how quickly patterns evolve. Change T*1.5 to T*0.5 for slower, more meditative motion. Increase it to T*3.0 for rapid morphing forms.
Rotation matrices determine how the coordinate space twists and folds. The current implementation uses cos(sin(T*0.2) - vec4(0,11,33,0)). Try replacing this with cos(T + vec4(0,11,33,0)) for smoother, more predictable rotations. Or use cos(T*T*0.1 - vec4(0,11,33,0)) for accelerating temporal dynamics.
Color mapping can shift entirely by modifying the hue function input. The current hue(l*l - T*0.5) creates colors that pulse with the geometric forms. Change it to hue(l + T) for colors that sweep through the spectrum independently of the geometry. Using hue(atan(uv.x, uv.y)) would map colors radially around the center point.
Domain repetition is controlled by the modulo operation mod(uv, 2.0) - 1.0. Changing the 2.0 to 4.0 doubles the size of the repeated cells, creating larger, more expansive patterns. Values below 1.0 create dense, intricate tiling.
Vignette intensity affects the edge darkening. The current power value of 0.3 in pow(vig, 0.3) creates a subtle gradient. Increase it to 0.8 for sharper falloff, or reduce it to 0.1 for barely perceptible edge dimming.
These aren't just parameter tweaks. Each modification reshapes the mathematical relationships that generate the forms, leading to genuinely different visual systems. A simple change to the rotation matrix phase can transform smooth spirals into jagged crystalline structures.
Different mathematical transformations create vastly different visual systems from the same rendering pipeline
The project separates concerns across several specialized classes:
Renderer manages the WebGL2 context, compiles shaders, and drives the render loop. It exposes methods for testing shader validity before deployment, preventing broken states from reaching the GPU.
PointerHandler normalizes mouse and touch input into the shader's coordinate system. It maintains a map of active pointers for multi-touch support and calculates movement deltas for interactive effects.
Editor provides the GLSL editing environment with keyboard handling for indentation, line breaks, and tab management. It uses DOM measurement to position error indicators at the correct line offsets.
Store persists shader source to localStorage using base64-encoded keys derived from the page location. This allows multiple shader projects to coexist without namespace collisions.
The render loop runs through requestAnimationFrame, passing current time, resolution, and pointer data as uniforms to the fragment shader. The shader has access to these inputs:
time- elapsed milliseconds converted to secondsresolution- canvas dimensions in pixelstouch- primary pointer coordinatespointers- array of all active touch/mouse positionspointerCount- number of simultaneous touchesmove- accumulated movement deltas
Open index.html in a modern browser that supports WebGL2. No build process, no dependencies, no server required. The experience runs entirely client-side.
If you want to work with the repository: git clone https://github.com/ionas/hallucinatory-phantasmagoria.git
MIT License - see LICENSE file for details.