Project_2026-03-31_02-20-53

GLSL shader by scry · created 2026-03-31 · updated 2026-04-01 · 10s loop · 5 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 timeo 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)

// === Sequencing parameters ===
const float SEQUENCE_DURATION = 48.0;  // total duration of one full sequence (4 passes)
const float COMB_DURATION = 3.0;      // duration of each comb pass
const int SEQUENCE_LOOPS = 8;         // number of times to repeat the sequence

// === Comb functions - discrete point forces ===

// Horizontal comb - row of dots dragging horizontally (direction: 1.0 = left-to-right, -1.0 = right-to-left)
vec2 combHorizontal(vec2 uv, float time, float spacing, float aspect, float direction, float offset) {
    direction = sign(direction);
    vec2 force = vec2(0.0);
    float radius = 0.015;
    float strength = 0.005;
    float onset = smoothstep(0.0, 0.5, time) * smoothstep(4.0, 3.0, time);

    for (float i = 0.0; i < 30.0; i++) {
        float y = i * spacing + spacing * 0.5 + offset;
        if (y > 1.0) break;
        vec2 tine = vec2(direction > 0.0 ? 0.0 : 1.0, y);
        tine.x += direction * time * 0.33;
        if (tine.x > 1.0) continue;  // skip if past right edge

        vec2 diff = uv - tine;
        diff.x *= aspect;
        float dist = length(diff);
        float inf = exp(-dist * dist / (radius * radius));
        force.x += direction * strength * inf * onset;
    }
    return force;
}

// Vertical comb - column of dots dragging vertically (direction: 1.0 = bottom-to-top, -1.0 = top-to-bottom)
vec2 combVertical(vec2 uv, float time, float spacing, float aspect, float direction, float offset) {
    direction = sign(direction);
    vec2 force = vec2(0.0);
    float radius = 0.015;
    float strength = 0.005;
    float onset = smoothstep(0.0, 0.5, time) * smoothstep(4.0, 3.0, time);

    for (float i = 0.0; i < 30.0; i++) {
        float x = i * spacing + spacing * 0.5 + offset;
        if (x > 1.0) break;
        vec2 tine = vec2(x, direction > 0.0 ? 0.0 : 1.0);
        tine.y += direction * time * 0.33;
        if (tine.y > 1.0) continue;

        vec2 diff = uv - tine;
        diff.x *= aspect;
        float dist = length(diff);
        float inf = exp(-dist * dist / (radius * radius));
        force.y += direction * strength * inf * onset;
    }
    return force;
}

// Nonpareil - horizontal dots then vertical dots
vec2 nonpareil(vec2 uv, float time, float spacing, float aspect) {
    vec2 force = vec2(0.0);
    float radius = 0.012;
    float strength = 0.005;

    // phase 1: horizontal drag
    float t1 = smoothstep(0.0, 0.5, time) * smoothstep(3.0, 2.0, time);
    for (float i = 0.0; i < 30.0; i++) {
        float y = (i + 0.5) * spacing;
        if (y > 1.0) break;
        float dir = mod(i, 2.0) * 2.0 - 1.0; // alternate direction
        vec2 tine = vec2(0.5 + dir * time * 0.06, y);
        tine.x = clamp(tine.x, 0.0, 1.0);

        vec2 diff = uv - tine;
        diff.x *= aspect;
        float dist = length(diff);
        force.x += dir * strength * exp(-dist * dist / (radius * radius)) * t1;
    }

    // phase 2: vertical drag
    float t2 = smoothstep(2.5, 3.0, time) * smoothstep(5.5, 5.0, time);
    for (float i = 0.0; i < 30.0; i++) {
        float x = (i + 0.5) * spacing;
        if (x > 1.0) break;
        float dir = mod(i, 2.0) * 2.0 - 1.0;
        vec2 tine = vec2(x, 0.5 + dir * (time - 2.5) * 0.06);
        tine.y = clamp(tine.y, 0.0, 1.0);

        vec2 diff = uv - tine;
        diff.x *= aspect;
        float dist = length(diff);
        force.y += dir * strength * exp(-dist * dist / (radius * radius)) * t2;
    }
    return force;
}

// Bouquet - ring of dots spiraling outward from center
vec2 bouquet(vec2 uv, float time, float aspect) {
    vec2 force = vec2(0.0);
    float radius = 0.02;
    float strength = 0.004;
    float onset = smoothstep(0.0, 1.0, time) * smoothstep(4.0, 3.0, time);

    for (float i = 0.0; i < 16.0; i++) {
        float angle = i / 16.0 * 6.2831853 + time * 0.5;
        float r = 0.05 + time * 0.04;
        vec2 tine = vec2(0.5) + vec2(cos(angle), sin(angle)) * r;

        vec2 diff = uv - tine;
        diff.x *= aspect;
        float dist = length(diff);
        vec2 dir = normalize(diff + 1e-6);
        // push outward + tangential for spiral
        vec2 tang = vec2(-dir.y, dir.x);
        force += (dir * 0.7 + tang * 0.3) * strength * exp(-dist * dist / (radius * radius)) * onset;
    }
    return force;
}

// Stone drops - single point pushes sequentially
vec2 stoneDrop(vec2 uv, float time, float aspect) {
    vec2 force = vec2(0.0);
    float radius = 0.03;
    float strength = 0.006;

    for (float i = 0.0; i < 7.0; i++) {
        float t = time - i * 0.8;
        if (t < 0.0 || t > 1.5) continue;
        vec2 center = vec2(
            fract(sin(i * 127.1) * 311.7),
            fract(sin(i * 269.5) * 183.3)
        );
        vec2 diff = uv - center;
        diff.x *= aspect;
        float dist = length(diff);
        float ripple = smoothstep(0.0, 0.3, t) * smoothstep(1.5, 0.8, t);
        vec2 dir = normalize(diff + 1e-6);
        force += dir * ripple * strength * exp(-dist * dist / (radius * radius));
    }
    return force;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord / iResolution.xy;
    vec2 texel = 1.0 / iResolution.xy;
    float aspect = iResolution.x / iResolution.y;

    if (iFrame < 5) {
        fragColor = vec4(0.0);
        return;
    }

    // self-advect velocity (MacCormack-lite: average forward and backward)
    vec2 vel = texture(iChannel0, uv).xy;
    vec2 backUV = uv - vel * texel;
    vec2 vel1 = texture(iChannel0, backUV).xy;
    vec2 fwdUV = backUV + vel1 * texel;
    vec2 velFwd = texture(iChannel0, fwdUV).xy;
    vel = vel1 + (vel - velFwd) * 0.5;

    // neighbor samples
    vec2 vL = texture(iChannel0, uv - vec2(texel.x, 0.0)).xy;
    vec2 vR = texture(iChannel0, uv + vec2(texel.x, 0.0)).xy;
    vec2 vD = texture(iChannel0, uv - vec2(0.0, texel.y)).xy;
    vec2 vU = texture(iChannel0, uv + vec2(0.0, texel.y)).xy;

    // divergence
    float div = (vR.x - vL.x + vU.y - vD.y) * 0.5;

    // pressure from neighbors
    float pL = texture(iChannel0, uv - vec2(texel.x, 0.0)).z;
    float pR = texture(iChannel0, uv + vec2(texel.x, 0.0)).z;
    float pD = texture(iChannel0, uv - vec2(0.0, texel.y)).z;
    float pU = texture(iChannel0, uv + vec2(0.0, texel.y)).z;
    float pressure = (pL + pR + pD + pU - div) * 0.25;

    // pressure gradient correction
    vel -= vec2(pR - pL, pU - pD) * 0.4;

    // diffusion (higher = smoother but less detail)
    vec2 velAvg = (vL + vR + vD + vU) * 0.25;
    vel = mix(vel, velAvg, 0.08);

    // vorticity confinement
    float cC = (vR.y - vL.y) - (vU.x - vD.x);
    float cL = texture(iChannel0, uv - vec2(texel.x*2.0, 0.0)).xy.y - vL.y;
    float cR = vR.y - texture(iChannel0, uv + vec2(texel.x*2.0, 0.0)).xy.y;
    float cD = texture(iChannel0, uv - vec2(0.0, texel.y*2.0)).xy.x - vD.x;
    float cU = vU.x - texture(iChannel0, uv + vec2(0.0, texel.y*2.0)).xy.x;

    vec2 absGrad = vec2(abs(cR) - abs(cL), abs(cU) - abs(cD));
    vec2 vortF = (length(absGrad) > 1e-5) ? normalize(absGrad) : vec2(0.0);
    vel += vortF * cC * 0.02;

    // damping
    vel *= 0.9999;

    // mouse force
    if (iMouse.z > 0.0) {
        vec2 mouse = iMouse.xy / iResolution.xy;
        vec2 prevMouse = texture(iChannel1, vec2(0.5)).xy;

        if (length(prevMouse) > 0.01) {
            vec2 mouseVel = (mouse - prevMouse) * 4.0;
            vec2 diff = uv - mouse;
            diff.x *= aspect;
            float dist = length(diff);
            float radius = 0.05;
            float influence = exp(-dist * dist / (radius * radius));
            vel += mouseVel * influence;
        }
    }
    
    // === SEQUENCED PROGRAM FORCE ===
    float teeth = 8.0;  // number of comb teeth, tweak this
    
    float toothSpacing = 1.0 / teeth;
    // Calculate looped time
    float loopTime = mod(iTime*1., SEQUENCE_DURATION * float(SEQUENCE_LOOPS));
    float sequenceTime = mod(loopTime, SEQUENCE_DURATION);
    vec2 cuv = uv;
    cuv += sin(vec2(0.,1.57)-timeo*4.)*0.02*(sin(timeo/3.)*0.5+0.5);
    vec2 cmult = vec2(1.,1.)*20.;
    // Sequence: vert>, vert<, horz^, horzv (4 passes)
    if (sequenceTime < COMB_DURATION) {
        // Pass 1: Vertical left-to-right
        vel += combVertical(cuv, sequenceTime, toothSpacing, aspect, 1.0, 0.0) * cmult.y;
    } else if (sequenceTime < COMB_DURATION * 2.0) {
        // Pass 2: Vertical right-to-left
        vel += combVertical(cuv, sequenceTime - COMB_DURATION, toothSpacing, aspect, -1.0, toothSpacing * 0.5) * cmult.y;
    } else if (sequenceTime < COMB_DURATION * 3.0) {
        // Pass 3: Horizontal bottom-to-top
        vel += combHorizontal(cuv, sequenceTime - COMB_DURATION * 2.0, toothSpacing*aspect, aspect, 1.0, 0.0) * cmult.x;
    } else {
        // Pass 4: Horizontal top-to-bottom
        vel += combHorizontal(cuv, sequenceTime - COMB_DURATION * 3.0, toothSpacing*aspect, aspect, -1.0, toothSpacing*aspect * 0.5) * cmult.x;
    }
    //*/
    // You can also sequence them:
    // if (iTime < 3.0) vel += combHorizontal(uv, iTime, teeth);
    // else if (iTime < 6.0) vel += combVertical(uv, iTime - 3.0, teeth);
    // else vel += bouquet(uv, iTime - 6.0, aspect);

    // boundary
    float border = 3.0;
    if (fragCoord.x < border || fragCoord.x > iResolution.x - border) vel.x = 0.0;
    if (fragCoord.y < border || fragCoord.y > iResolution.y - border) vel.y = 0.0;

    fragColor = vec4(vel, pressure, 1.0);
}

Buffer B (iChannel1)

// Buffer B - previous mouse position
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    fragColor = (iMouse.z > 0.0)
        ? vec4(iMouse.xy / iResolution.xy, 0.0, 1.0)
        : vec4(0.0);
}

Buffer C (iChannel2)

// Buffer C - Advected UV coordinates

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord / iResolution.xy;
    vec2 texel = 1.0 / iResolution.xy;

    // initialize to identity - give it a few frames to settle
    if (iFrame < 5) {
        fragColor = vec4(uv, 0.0, 1.0);
        return;
    }

    // read velocity from Buffer A
    vec2 vel = texture(iChannel0, uv).xy;  // BufferA in ShaderKit

    // only advect if velocity is sane
    vec2 srcUV = uv;
    if (length(vel) < 10.0) {
        srcUV = uv - vel * texel * 1.5;
    }

    // bilinear lookup of stored UV at source
    vec2 paintUV = texture(iChannel2, srcUV).xy;  // BufferC (self) in ShaderKit

    // clamp to prevent runaway
    paintUV = clamp(paintUV, 0.0, 1.0);

    fragColor = vec4(paintUV, 0.0, 1.0);
}

Buffer D (iChannel3)

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord.xy / iResolution.xy;
    vec3 col = vec3(uv, 0.5 + 0.5*sin(iTime))*0.;
    col += texture(iChannel0, uv).rgb*5.+0.5;
    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