Lorenz Attractor

GLSL shader by scry · created 2026-02-06 · 10s loop · 3 passes

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*2.));
            //p = vec3(cos(angle1) * sin(angle2), sin(angle1) * sin(angle2), cos(angle2)) * 5.0;
        }
        
        // Step forward
        p = lorenzStep(p, 0.001);
        
        // 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);
    
    // 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 = 80.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 = 200;
    for (int t = 1; t < 200; 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

Browse all public shaders · All shaders by scry · ShaderKit home

Vibe Mode BETA
💰 ~0 credits
Uniforms
FPS: 0
Time: 0
Resolution: 0 x 0

Account

FPS: -- Time: -- --×-- 1x