Coupled Oscillator Automata

GLSL shader by scry · created 2026-01-08 · updated 2026-01-12 · 10s loop · 4 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)

#define NEIGHBOR_MODE 2 // 0=4-dir, 1=3x3, 2=5x5, 3=7x7, 4=9x9

vec3 px2(vec2 uv) {
    return texture(iChannel2,uv).rgb;
}
vec3 px1(vec2 uv) {
    return texture(iChannel1,uv).rgb;
}
vec3 px(vec2 uv) {
    return texture(iChannel0,uv).rgb;
}
vec3 getPhase(vec2 uv) {
    return px(uv)*pi*2.;
}

vec3 getPhaseDifference(vec2 uv, float angle) {
    vec2 texel = 1.0 / iResolution.xy;
    vec3 center = getPhase(uv);
    vec3 diff = vec3(0.);
    float count = 0.;
    
#if NEIGHBOR_MODE == 0
    // 4-directional (up/down/left/right)
    for (int i = 0; i < 4; i++) {
        vec2 offset = vec2(i == 0 ? 1. : (i == 1 ? -1. : 0.),
                           i == 2 ? 1. : (i == 3 ? -1. : 0.));
        vec2 rotatedOffset = offset * r2d(angle);
        diff += sin(getPhase(uv + rotatedOffset * texel) - center);
        count += 1.;
    }
#else
    #if NEIGHBOR_MODE == 1
        const int radius = 1; // 3x3
    #elif NEIGHBOR_MODE == 2
        const int radius = 2; // 5x5
    #elif NEIGHBOR_MODE == 3
        const int radius = 3; // 7x7
    #elif NEIGHBOR_MODE == 4
        const int radius = 4; // 9x9
    #else
        const int radius = 1;
    #endif
    
    for (int y = -radius; y <= radius; y++) {
        for (int x = -radius; x <= radius; x++) {
            if (x == 0 && y == 0) continue;
            vec2 offset = vec2(float(x), float(y));
            vec2 rotatedOffset = offset * r2d(angle);
            diff += sin(getPhase(uv + rotatedOffset * texel) - center);
            count += 1.;
        }
    }
#endif
    
    diff /= count;
    return diff;
}

vec3 getNeighbors(vec2 uv) {
    return getPhase(uv) + getPhaseDifference(uv, 0.);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord.xy / iResolution.xy;
    vec2 tv = uv;
    uv -= 0.5;
    uv.x *= ar;
    vec3 col = vec3(0.);
    vec3 phase = getPhase(tv);
    vec3 neighbors = getNeighbors(tv);
    
    // Calculate curl (rotation) from phase gradients
    vec2 texel = 1.0 / iResolution.xy;
    //vec3 dx = getPhase(tv + vec2(texel.x, 0.)) - getPhase(tv - vec2(texel.x, 0.));
    //vec3 dy = getPhase(tv + vec2(0., texel.y)) - getPhase(tv - vec2(0., texel.y));
    //vec3 curl = vec3(dy.x - dx.y, dy.y - dx.z, dy.z - dx.x);
    
    //phase += 0.01 + curl * 0.001;
    vec3 phase1 = px1(tv)*pi*2.;
    vec3 phase2 = px2(tv)*pi*2.;
    phase = phase+getPhaseDifference(tv, 45.) + sin(phase1 - phase) * 0.002;
    phase += 0.02;
    phase += sin(phase2 - phase) * 0.1; // subtle influence from iChannel2
    //phase -= curl*0.1;
    phase = mix(phase*1.05-0.005,phase1*1.04-0.5,0.03);
    if (iFrame < 10) {
        phase = (uv.xxx*4.+uv.y*9.+sin(uv.x*30.)+sin(uv.y*30.))*2.;
        //phase = sin(uv.x)*0.2+uv.xxx;
    }
    
    
    phase += smoothstep(0.1,0.,length(uv-nm+0.5))*M.z*1.;
    col = fract(phase/(pi*2.));
    fragColor = vec4(col, 1.0);
}

Buffer B (iChannel1)

#define NEIGHBOR_MODE 4 // 0=4-dir, 1=3x3, 2=5x5, 3=7x7, 4=9x9
vec3 px0(vec2 uv) {
    return texture(iChannel0,uv).rgb;
}
vec3 px(vec2 uv) {
    return texture(iChannel1,uv).rgb;
}
vec3 getPhase(vec2 uv) {
    return px(uv)*pi*2.;
}

vec3 getPhaseDifference(vec2 uv, float angle) {
    vec2 texel = 1.0 / iResolution.xy;
    vec3 center = getPhase(uv);
    vec3 diff = vec3(0.);
    float count = 0.;
    
#if NEIGHBOR_MODE == 0
    // 4-directional (up/down/left/right)
    for (int i = 0; i < 4; i++) {
        vec2 offset = vec2(i == 0 ? 1. : (i == 1 ? -1. : 0.),
                           i == 2 ? 1. : (i == 3 ? -1. : 0.));
        vec2 rotatedOffset = offset * r2d(angle);
        diff += sin(getPhase(uv + rotatedOffset * texel) - center);
        count += 1.;
    }
#else
    #if NEIGHBOR_MODE == 1
        const int radius = 1; // 3x3
    #elif NEIGHBOR_MODE == 2
        const int radius = 2; // 5x5
    #elif NEIGHBOR_MODE == 3
        const int radius = 3; // 7x7
    #elif NEIGHBOR_MODE == 4
        const int radius = 4; // 9x9
    #else
        const int radius = 1;
    #endif
    
    for (int y = -radius; y <= radius; y++) {
        for (int x = -radius; x <= radius; x++) {
            if (x == 0 && y == 0) continue;
            vec2 offset = vec2(float(x), float(y));
            vec2 rotatedOffset = offset * r2d(angle);
            diff += sin(getPhase(uv + rotatedOffset * texel) - center);
            count += 1.;
        }
    }
#endif
    
    diff /= count;
    return diff;
}

vec3 getNeighbors(vec2 uv) {
    return getPhase(uv) + getPhaseDifference(uv, 0.);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord.xy / iResolution.xy;
    vec2 tv = uv;
    uv -= 0.5;
    uv.x *= ar;
    vec3 col = vec3(0.);
    vec3 phase = getPhase(tv);
    vec3 neighbors = getNeighbors(tv);
    
    // Calculate curl (rotation) from phase gradients
    vec2 texel = 1.0 / iResolution.xy;
    //vec3 dx = getPhase(tv + vec2(texel.x, 0.)) - getPhase(tv - vec2(texel.x, 0.));
    //vec3 dy = getPhase(tv + vec2(0., texel.y)) - getPhase(tv - vec2(0., texel.y));
    //vec3 curl = vec3(dy.x - dx.y, dy.y - dx.z, dy.z - dx.x);
    
    //phase += 0.01 + curl * 0.001;
    vec3 phase0 = px0(tv)*pi*2.;
    phase = phase+getPhaseDifference(tv, -45.) + sin(phase0 - phase) * 0.003;
    phase -= 0.02;
    //phase -= curl*0.1;
    
    if (iFrame < 2) {
        phase = (uv.xxx*4.+uv.y*9.+sin(uv.x*30.)+sin(uv.y*30.))*2.;
        //phase = sin(uv.x)*0.2+uv.xxx;
    }
    
    
    phase += smoothstep(0.1,0.,length(uv-nm+0.5))*M.z*1.;
    col = fract(phase/(pi*2.));
    fragColor = vec4(col, 1.0);
}

Buffer C (iChannel2)

#define NEIGHBOR_MODE 1 // 0=4-dir, 1=3x3, 2=5x5, 3=7x7, 4=9x9
vec3 px0(vec2 uv) {
    return texture(iChannel0,uv).rgb;
}
vec3 px1(vec2 uv) {
    return texture(iChannel1,uv).rgb;
}
vec3 px(vec2 uv) {
    return texture(iChannel2,uv).rgb;
}
vec3 getPhase(vec2 uv) {
    return px(uv)*pi*2.;
}

vec3 getPhaseDifference(vec2 uv, float angle) {
    vec2 texel = 1.0 / iResolution.xy;
    vec3 center = getPhase(uv);
    vec3 diff = vec3(0.);
    float count = 0.;
    
#if NEIGHBOR_MODE == 0
    // 4-directional (up/down/left/right)
    for (int i = 0; i < 4; i++) {
        vec2 offset = vec2(i == 0 ? 1. : (i == 1 ? -1. : 0.),
                           i == 2 ? 1. : (i == 3 ? -1. : 0.));
        vec2 rotatedOffset = offset * r2d(angle);
        diff += sin(getPhase(uv + rotatedOffset * texel) - center);
        count += 1.;
    }
#else
    #if NEIGHBOR_MODE == 1
        const int radius = 1; // 3x3
    #elif NEIGHBOR_MODE == 2
        const int radius = 2; // 5x5
    #elif NEIGHBOR_MODE == 3
        const int radius = 3; // 7x7
    #elif NEIGHBOR_MODE == 4
        const int radius = 4; // 9x9
    #else
        const int radius = 1;
    #endif
    
    for (int y = -radius; y <= radius; y++) {
        for (int x = -radius; x <= radius; x++) {
            if (x == 0 && y == 0) continue;
            vec2 offset = vec2(float(x), float(y));
            vec2 rotatedOffset = offset * r2d(angle);
            diff += sin(getPhase(uv + rotatedOffset * texel) - center);
            count += 1.;
        }
    }
#endif
    
    diff /= count;
    return diff;
}

vec3 getNeighbors(vec2 uv) {
    return getPhase(uv) + getPhaseDifference(uv, 0.);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord.xy / iResolution.xy;
    vec2 tv = uv;
    uv -= 0.5;
    uv.x *= ar;
    vec3 col = vec3(0.);
    vec3 phase = getPhase(tv);
    vec3 neighbors = getNeighbors(tv);
    
    // Calculate curl (rotation) from phase gradients
    vec2 texel = 1.0 / iResolution.xy;
    //vec3 dx = getPhase(tv + vec2(texel.x, 0.)) - getPhase(tv - vec2(texel.x, 0.));
    //vec3 dy = getPhase(tv + vec2(0., texel.y)) - getPhase(tv - vec2(0., texel.y));
    //vec3 curl = vec3(dy.x - dx.y, dy.y - dx.z, dy.z - dx.x);
    
    //phase += 0.01 + curl * 0.001;
    vec3 phase0 = px0(tv)*pi*2.;
    vec3 phase1 = px1(tv)*pi*2.;
    phase = phase+getPhaseDifference(tv, -4.) + sin(phase0 - phase) * 0.003;
    phase += 0.02;
    phase += sin(phase1 - phase) * 0.02; // subtle influence from iChannel1
    //phase -= curl*0.1;
    
    if (iFrame < 2) {
        phase = (uv.xxx*4.+uv.y*9.+sin(uv.x*30.)+sin(uv.y*30.))*2.;
        //phase = sin(uv.x)*0.2+uv.xxx;
    }
    
    
    phase += smoothstep(0.1,0.,length(uv-nm+0.5))*M.z*1.;
    col = fract(phase/(pi*2.));
    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