Lorenz Attractor 2
GLSL shader by scry · created 2026-02-06 · updated 2026-02-12 · 10s loop · 3 passes
Lorenz impsum
This page opens the shader in the ShaderKit browser GLSL editor: edit it live, fork it, or render it to video, GIF or images up to 8K. Also available as a full-screen view and an embeddable player.
Shader source (GLSL)
Common
#define pi acos(-1.)
#define deg pi/180. //1 degree
#define time iTime*2.*pi/10. //sin(time) loops 10 seconds
#define R iResolution.xy //shorthand
#define ar R.x/R.y //aspect ratio
#define M iMouse //shorthand
#define xm (M.xy/R) //normalized mouse
#define nm ((xm.xy-0.5)*vec2(ar,1.)+0.5) //aspect ratio correction
vec3 cs = vec3(1.,2.,3.);
mat2 r2d(float a) {
return mat2(cos(a),sin(a),-sin(a),cos(a));
}
Buffer A (iChannel0)
// Lorenz attractor parameters
float SIGMA = 10.0;
float RHO = 28.0;
float BETA = 8.0/3.0;
// Lorenz system derivatives
vec3 lorenzDerivative(vec3 p) {
return vec3(
SIGMA * (p.y - p.x),
p.x * (RHO - p.z) - p.y,
p.x * p.y - BETA * p.z
);
}
// Simple Runge-Kutta integration
vec3 lorenzStep(vec3 p, float dt) {
vec3 k1 = lorenzDerivative(p);
vec3 k2 = lorenzDerivative(p + k1 * dt * 0.5);
vec3 k3 = lorenzDerivative(p + k2 * dt * 0.5);
vec3 k4 = lorenzDerivative(p + k3 * dt);
return p + (k1 + 2.0*k2 + 2.0*k3 + k4) * dt / 6.0;
}
// Project 3D point to 2D screen
vec2 project(vec3 p, vec3 camPos, float zoom) {
vec3 dir = normalize(p - camPos) * length(p - camPos);
float dist = length(dir);
vec2 screen = dir.xy / (dir.z + 50.0) * zoom;
return screen;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord.xy / iResolution.xy;
// Mouse controls
vec2 mouse = iMouse.xy / iResolution.xy;
if (iMouse.z > 0.0) {
RHO = 10.0 + mouse.x * 40.0; // 10-50
SIGMA = 5.0 + mouse.y * 20.0; // 5-25
}
// Read previous state
vec4 prev = texture(iChannel0, uv);
vec3 col = prev.rgb * 0.95; // Fade trails
// Particle ID from UV
float particleId = floor(uv.x * iResolution.x) + floor(uv.y * iResolution.y) * iResolution.x;
{
// Decode state from previous frame
vec3 p = prev.rgb * 50.0 - 25.0; // Decode from [0,1] to Lorenz space
// Initialize on first frame or reset
if (iFrame < 2 || length(p) < 0.1) {
// Spread particles in a small sphere
float angle1 = particleId * 2.399;
float angle2 = particleId * 1.618;
p = vec3(sin(cs+uv.xxy*3.)*0.5+0.5)*20.;
//p = vec3(cos(angle1) * sin(angle2), sin(angle1) * sin(angle2), cos(angle2)) * 5.0;
}
// Step forward
p = lorenzStep(p, 0.005);
// Encode state back to [0,1]
col = (p + 25.0) / 50.0;
}
fragColor = vec4(col, 1.0);
}
Buffer B (iChannel1)
// Project 3D point to 2D screen with camera
vec2 project(vec3 p, vec3 camPos, vec3 camTarget, float zoom) {
// Build camera basis
vec3 forward = normalize(camTarget - camPos);
vec3 right = normalize(cross(forward, vec3(0.0, 1.0, 0.0)));
vec3 up = cross(right, forward);
// Transform point to camera space
vec3 relPos = p - camPos;
vec3 camSpace = vec3(
dot(relPos, right),
dot(relPos, up),
dot(relPos, forward)
);
// Perspective projection
if (camSpace.z <= 0.0) return vec2(1000.0); // Behind camera
vec2 screen = camSpace.xy / camSpace.z * zoom;
return screen;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 uv = fragCoord.xy / iResolution.xy;
vec2 screenUV = uv;
screenUV -= 0.5;
screenUV.x *= ar;
vec3 col = vec3(0.0);
//screenUV.y -= 0.15;
// Mouse orbit camera
// Read saved camera state from corner pixel
vec2 savedState = texture(iChannel1, vec2(0.5/iResolution.x, 0.5/iResolution.y)).rg;
float orbitAngle = savedState.r * 6.28318;
float orbitHeight = (savedState.g - 0.5) * 40.0;
float orbitDist = 140.0;
// Update camera state if mouse is pressed
vec2 mouse = iMouse.xy / iResolution.xy;
if (iMouse.z > 0.0) {
orbitAngle = (mouse.x - 0.5) * 6.28318; // Full rotation
orbitHeight = (mouse.y - 0.5) * 40.0; // -20 to +20
}
//orbitAngle += sin(time);
// Save camera state to corner pixel
if (fragCoord.x < 1.0 && fragCoord.y < 1.0) {
fragColor = vec4(orbitAngle / 6.28318, orbitHeight / 40.0 + 0.5, 0.0, 1.0);
return;
}
orbitAngle += time/4.;
screenUV.x -= sin(orbitAngle)*0.33;
// Build camera from orbit parameters
vec3 camPos = vec3(
sin(orbitAngle) * orbitDist,
orbitHeight,
cos(orbitAngle) * orbitDist
);
vec3 camTarget = vec3(0.0, 0.0, 0.0);
float zoom = 1.5;
// Visualize particle states from iChannel0
int numSamples = 600;
for (int t = 0; t < 600; t++) {
float sampleIdx = float(t);
// Sample particle position from buffer
vec2 sampleUV = vec2(
(sampleIdx + 0.5) / 5.0,
0.5
);
// Distribute samples across texture
sampleUV.x = mod(sampleIdx, iResolution.x) / iResolution.x;
sampleUV.y = floor(sampleIdx / iResolution.x) / iResolution.y;
sampleUV += 0.5 / iResolution.xy;
vec3 encoded = texture(iChannel0, sampleUV).rgb;
vec3 p = encoded * 50.0 - 25.0; // Decode from [0,1] to Lorenz space
// Skip if uninitialized
if (length(p) < 0.1) continue;
// Project to screen with camera
vec2 pos2D = project(p, camPos, camTarget, zoom);
// Draw particle
float d = length(screenUV - pos2D);
float particleSize = 0.005;
// Color based on Z coordinate
vec3 particleCol = 0.5 + 0.5 * cos(6.28318 * (p.z * 0.02 + vec3(0.0, 0.33, 0.67)));
col += particleCol * smoothstep(particleSize, 0.0, d) * 0.15;
//col += particleCol * 0.01 / (d + 0.01); // Glow
}
col += texture(iChannel1,uv).rgb*0.98;
fragColor = vec4(col, 1.0);
}
Image
Not used
More shaders by scry
- Project_2026-08-15_18-22-06
- Project_2026-08-14_16-45-00
- Project_2026-07-24_15-10-23
- Project_2026-07-15_03-18-26
- Glass of Orange Juice (raymarched)
- Project_2026-07-13_00-53-50
- Project_2026-07-10_16-39-54
- Popcorn ceiling
- Project_2026-06-20_15-32-19
- Project_2026-06-25_12-22-43
- Visual Enhancer
- Project_2026-06-24_11-11-39
Browse all public shaders · All shaders by scry · ShaderKit home
Unread comments
×Projects with new comments. Click to open and read.