Project_2026-02-08_00-06-39

GLSL shader by scry · created 2026-02-08 · updated 2026-02-09 · 10s loop · 7 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));
}


// Linear to sRGB
vec3 linearToSRGB(vec3 c) {
    return mix(
        c * 12.92,
        1.055 * pow(c, vec3(1.0/2.4)) - 0.055,
        step(0.0031308, c)
    );
}

// sRGB to linear
vec3 sRGBToLinear(vec3 c) {
    return mix(
        c / 12.92,
        pow((c + 0.055) / 1.055, vec3(2.4)),
        step(0.04045, c)
    );
}

// HSV to RGB
vec3 hsv2rgb(vec3 c) {
    vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
    vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
    return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}

// RGB to HSV
vec3 rgb2hsv(vec3 c) {
    vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
    vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
    vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
    float d = q.x - min(q.w, q.y);
    float e = 1.0e-10;
    return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}

// Color palette (common technique for shaders)
vec3 palette(float t, vec3 a, vec3 b, vec3 c, vec3 d) {
    return a + b * cos(6.28318 * (c * t + d));
}

// Luminance (perceived brightness)
float luminance(vec3 color) {
    return dot(color, vec3(0.299, 0.587, 0.114));
}

// Mix colors in HSV space
vec3 mixHSV(vec3 c1, vec3 c2, float t) {
    vec3 hsv1 = rgb2hsv(c1);
    vec3 hsv2 = rgb2hsv(c2);
    vec3 hsv = mix(hsv1, hsv2, t);
    return hsv2rgb(hsv);
}

Buffer A (iChannel0)

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord.xy / iResolution.xy;
    
    vec2 velocity = vec2(0.0);
    // Initialize or update particle position
    vec2 particlePos = texture(iChannel0, uv).xy;
    
    // First frame initialization
    if (iFrame < 1) {
        // Initialize particle in circular pattern based on UV
        float angle = uv.x * 2.0 * pi;
        float radius = 0.3;
        particlePos = vec2(cos(angle), sin(angle)) * radius;
    } else {
        // Update particle: orbit around center
        vec2 center;
        if (iMouse.z > 0.5) {
            center = (xm - 0.5) * vec2(ar, 1.0);
        } else {
            center = vec2(0.0);
        }
        vec2 toCenter = center - particlePos;
        float dist = length(toCenter);
        
        // Attraction force towards center
        float attractionStrength = 0.00002+iMouse.z*0.8;
        
        // Tiny repulsion from exact center to prevent collapse
        if (dist < 0.05) {
            attractionStrength *= -0.3;
        }
        vec2 attraction = normalize(toCenter) * attractionStrength * 0.016;
        
        // Apply attraction
        velocity += attraction;
        
        // Repulsion from walls in iChannel2
        vec2 pixelSize = 1.0 / iResolution.xy;
        float wallThreshold = 0.3; // Values above this are considered walls
        
        // Sample iChannel2 at particle position
        vec2 particleUV = (particlePos / vec2(ar, 1.0)) + 0.5;
        float centerWall = texture(iChannel2, particleUV).r;
        
        // Sample gradient around particle to find wall direction
        vec2 wallGradient = vec2(0.);
        float sampleRadius = 3.0;
        for (float dy = -1.0; dy <= 1.0; dy += 1.0) {
            for (float dx = -1.0; dx <= 1.0; dx += 1.0) {
                if (dx == 0.0 && dy == 0.0) continue;
                vec2 offset = vec2(dx, dy) * pixelSize * sampleRadius;
                float wallValue = texture(iChannel2, particleUV + offset).r;
                if (wallValue > wallThreshold) {
                    wallGradient += vec2(dx, dy) * wallValue;
                }
            }
        }
        
        // Apply mild repulsion away from walls
        float repulsionMildness = 0.002;
        velocity -= normalize(wallGradient + vec2(0.00001)) * length(wallGradient) * repulsionMildness;
        
        // Attraction to walls in iChannel4
        vec2 wallGradient4 = vec2(0.);
        for (float dy = -1.0; dy <= 1.0; dy += 1.0) {
            for (float dx = -1.0; dx <= 1.0; dx += 1.0) {
                if (dx == 0.0 && dy == 0.0) continue;
                vec2 offset = vec2(dx, dy) * pixelSize * sampleRadius;
                float wallValue = texture(iChannel4, particleUV + offset).r;
                if (wallValue > wallThreshold) {
                    wallGradient4 += vec2(dx, dy) * wallValue;
                }
            }
        }
        
        // Apply attraction towards iChannel4 walls
        float attractionStrength4 = -0.0005;
        velocity += normalize(wallGradient4 + vec2(0.00001)) * length(wallGradient4) * attractionStrength4;
        
        // Attraction/repulsion from other particles
        float repulsionStrength = +0.00004;
        for (float i = 0.0; i < 1.0; i += 0.01) {
            vec2 otherUV = vec2(i, 0.5);
            if (abs(otherUV.x - uv.x) < 0.001) continue; // Skip self
            
            vec2 otherPos = texture(iChannel0, otherUV).xy;
            vec2 toOther = particlePos - otherPos;
            float otherDist = length(toOther);
            if (otherDist > 0.001) {
                // Attraction at medium/long range
                if (otherDist > 0.03) {
                    velocity -= normalize(toOther) * 0.000002 / (otherDist + 0.1);
                } else {
                    // Repulsion at close range to prevent collapse
                    velocity += normalize(toOther) * repulsionStrength / (otherDist * otherDist + 0.02);
                }
            }
        }
        particlePos += velocity;
    }
    
    // Store particle position in RG channels
    fragColor = vec4(particlePos, 0.0, 1.0);
}

Buffer B (iChannel1)

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord.xy / iResolution.xy;
    vec2 p = (uv - 0.5) * vec2(ar, 1.0);
    
    // Resolution-independent particle size
    float particleSize = 5.0 / iResolution.y;
    
    vec3 col = vec3(0.0);
    
    // Sample all particles from iChannel0 and render
    for (float i = 0.0; i < 1.0; i += 0.01) {
        vec2 sampleUV = vec2(i, 0.5);
        vec2 particlePos = texture(iChannel0, sampleUV).xy;
        
        // Draw particle as circle
        float dist = length(p - particlePos);
        float particle = smoothstep(particleSize * 1.3, particleSize * 0.2, dist);
        col += vec3(1.0, 0.5, 0.2) * particle;
    }
    
    col += vec3(0.02, 0.03, 0.05); // Background
    fragColor = vec4(col, 1.0);
}

Buffer C (iChannel2)

// Hash function for noise
float hash(vec2 p) {
    return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord.xy / iResolution.xy;
    
    // Get previous state
    float state = texture(iChannel2, uv).r;
    
    // Initialize on first frame with random noise
    if (iFrame < 2) {
    //if (mod(float(iFrame), 520.) == 0.) {
        //state = hash(uv * 100.0 + vec2(0.1));
        state *= 0.;
    } else {
        // Sample neighborhood (3x3 kernel)
        float sum = 0.0;
        vec2 pixelSize = 1.0 / iResolution.xy;
        
        for (int y = -1; y <= 1; y++) {
            for (int x = -1; x <= 1; x++) {
                vec2 offset = vec2(float(x), float(y)) * pixelSize;
                sum += texture(iChannel2, uv + offset).r;
            }
        }
        
        // Smooth cellular automata rule
        float avg = sum / 9.0;
        // Smooth transition: values near 0.5 survive, extremes die
        state = smoothstep(0.24, 0.42, avg) * (1.0 - smoothstep(0.2, 0.7, avg));
        state = mix(state, avg, 0.2); // Blend with average for smoothness
    }
    
    vec3 col = vec3(state);
    col += texture(iChannel1,uv).rrr*0.3;
    fragColor = vec4(col, 1.0);
}

Buffer D (iChannel3)

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord.xy / iResolution.xy;
    vec3 col = texture(iChannel2,uv).rgb;
    vec3 b = texture(iChannel3,uv).rgb;
    col = clamp(col-0.02,0.,1.);
    col += b*0.9;
    //col += mix(col,b,0.9);
    fragColor = vec4(col, 1.0);
}

Buffer E (iChannel4)

// Hash function for noise
float hash(vec2 p) {
    return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord.xy / iResolution.xy;
    
    // Get previous state
    float state = texture(iChannel4, uv).r;
    
    // Initialize on first frame with random noise
    if (iFrame < 2) {
    //if (mod(float(iFrame), 520.) == 0.) {
        state = hash(uv * 100.0 + vec2(0.1));
        //state *= 0.;
    } else {
        // Sample neighborhood (3x3 kernel)
        float sum = 0.0;
        vec2 pixelSize = 1.0 / iResolution.xy;
        
        for (int y = -1; y <= 1; y++) {
            for (int x = -1; x <= 1; x++) {
                vec2 offset = vec2(float(x), float(y)) * pixelSize;
                sum += texture(iChannel4, uv + offset).r;
            }
        }
        
        // Smooth cellular automata rule
        float avg = sum / 9.0;
        // Smooth transition: values near 0.5 survive, extremes die
        state = smoothstep(0.25, 0.399, avg) * (1.0 - smoothstep(0.22, 0.7, avg));
        state = mix(state, avg, 0.3); // Blend with average for smoothness
    }
    
    vec3 col = vec3(state);
    col -= texture(iChannel5,uv).rrr*0.3;
    col += texture(iChannel2,uv).rrr;
    fragColor = vec4(col, 1.0);
}

Buffer F (iChannel5)

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord.xy / iResolution.xy;
    vec2 p = (uv - 0.5) * vec2(ar, 1.0);
    
    // Resolution-independent particle size
    float particleSize = 5.0 / iResolution.y;
    
    vec3 col = vec3(0.0);
    
    // Sample all particles from iChannel0 and render
    for (float i = 0.0; i < 1.0; i += 0.01) {
        vec2 sampleUV = vec2(i, 0.5);
        vec2 particlePos = texture(iChannel0, sampleUV).xy;
        
        // Draw particle as circle
        float dist = length(p - particlePos);
        float particle = smoothstep(particleSize * 2., particleSize * 1., dist);
        col += vec3(1.0, 0.5, 0.2) * particle;
    }
    
    col += vec3(0.02, 0.03, 0.05); // Background
    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