Back to shaders

Shader test bench

20260127

glsl-daily-practice generative glsl runnable fragment MIT
Source
runnable fragment

Complete GLSL fragment shader. Stronghold runs it directly when the browser can compile it.

Code

precision mediump float;
#define TDOutputSwizzle(c) (c)
// TouchDesigner GLSL TOP - Sacred Geometry: Celestial Temple
// Deep violet → celestial blue | Ancient math + otherworldly beauty
// Domain warping, aurora, mandala, god rays, chromatic bloom

uniform float u_time;
#define fragColor gl_FragColor

#define PI  3.14159265359
#define TAU 6.28318530718
#define PHI 1.61803398875

// ── Palette ────────────────────────────────────────────────
vec3 pal[10] = vec3[10](
    vec3(0.212, 0.176, 0.471),  // #362d78
    vec3(0.322, 0.247, 0.639),  // #523fa3
    vec3(0.569, 0.424, 0.800),  // #916ccc
    vec3(0.741, 0.631, 0.898),  // #bda1e5
    vec3(0.784, 0.753, 0.914),  // #c8c0e9
    vec3(0.518, 0.729, 0.906),  // #84bae7
    vec3(0.318, 0.416, 0.831),  // #516ad4
    vec3(0.200, 0.247, 0.529),  // #333f87
    vec3(0.161, 0.188, 0.224),  // #293039
    vec3(0.157, 0.212, 0.192)   // #283631
);

vec3 getColor(float t) {
    t = fract(t) * 9.0;
    int i = int(floor(t));
    float f = fract(t);
    f = f * f * (3.0 - 2.0 * f);
    return mix(pal[i], pal[min(i + 1, 9)], f);
}

// Bright accent blend from palette
vec3 accentColor(float t, float time) {
    return mix(
        mix(pal[2], pal[5], 0.5 + 0.5 * sin(t * 3.0 + time * 0.3)),
        mix(pal[3], pal[6], 0.5 + 0.5 * cos(t * 2.0 + time * 0.2)),
        0.5 + 0.5 * sin(t * 5.0 + time * 0.5)
    );
}

// ── Utilities ──────────────────────────────────────────────
mat2 rot(float a) {
    float c = cos(a), s = sin(a);
    return mat2(c, -s, s, c);
}

float hash21(vec2 p) {
    p = fract(p * vec2(234.34, 435.345));
    p += dot(p, p + 34.23);
    return fract(p.x * p.y);
}

float hash11(float p) {
    p = fract(p * 0.1031);
    p *= p + 33.33;
    return fract(p * (p + p));
}

float noise(vec2 p) {
    vec2 i = floor(p), f = fract(p);
    f = f * f * (3.0 - 2.0 * f);
    return mix(
        mix(hash21(i), hash21(i + vec2(1, 0)), f.x),
        mix(hash21(i + vec2(0, 1)), hash21(i + vec2(1, 1)), f.x),
        f.y
    );
}

float fbm(vec2 p) {
    float v = 0.0, a = 0.5;
    mat2 r = rot(0.5);
    for (int i = 0; i < 6; i++) {
        v += a * noise(p);
        p = r * p * 2.0 + 1.7;
        a *= 0.5;
    }
    return v;
}

// ── Domain warping (creates organic flowing distortion) ────
vec2 domainWarp(vec2 p, float time) {
    float n1 = fbm(p + vec2(time * 0.06, time * 0.08));
    float n2 = fbm(p + vec2(time * -0.07, time * 0.05) + 5.2);
    vec2 warp1 = vec2(n1, n2) * 0.4;
    // Second pass for deeper warp
    float n3 = fbm(p + warp1 + vec2(time * 0.03));
    float n4 = fbm(p + warp1 + vec2(time * -0.04) + 8.1);
    return vec2(n3, n4) * 0.3;
}

// ── SDF primitives ─────────────────────────────────────────
float sdCircle(vec2 p, float r) { return length(p) - r; }

float sdSegment(vec2 p, vec2 a, vec2 b) {
    vec2 pa = p - a, ba = b - a;
    float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
    return length(pa - ba * h);
}

// ── Flower of Life (breathing + ripple) ────────────────────
float flowerOfLife(vec2 p, float r, float thick, float t) {
    float d = 1e9;
    float breathe = 1.0 + 0.06 * sin(t * 0.8);
    float rb = r * breathe;
    d = min(d, abs(sdCircle(p, rb)));
    for (int ring = 0; ring < 3; ring++) {
        int count = (ring == 0) ? 6 : 6;
        float ringR = (ring == 0) ? rb : (ring == 1) ? rb * 1.732 : rb * 2.0;
        float angleOff = (ring == 1) ? PI / 6.0 : 0.0;
        for (int i = 0; i < 6; i++) {
            float a = float(i) * TAU / 6.0 + angleOff;
            float wave = sin(t * 1.2 + float(i + ring * 6) * 0.4) * 0.025;
            vec2 c = (ringR + wave) * vec2(cos(a), sin(a));
            d = min(d, abs(sdCircle(p - c, rb)));
        }
    }
    return d - thick;
}

// ── Metatron's Cube (orbiting nodes) ───────────────────────
float metatronsCube(vec2 p, float r, float thick, float t) {
    float d = 1e9;
    vec2 pts[13];
    pts[0] = vec2(0.0);
    for (int i = 0; i < 6; i++) {
        float a = float(i) * TAU / 6.0 + t * 0.05;
        float r1 = r * (1.0 + 0.05 * sin(t * 0.6 + float(i)));
        float r2 = 2.0 * r * (1.0 + 0.04 * sin(t * 0.5 + float(i) * 0.8));
        pts[1 + i] = r1 * vec2(cos(a), sin(a));
        pts[7 + i] = r2 * vec2(cos(a), sin(a));
    }
    for (int i = 0; i < 13; i++) {
        float pulse = 1.0 + 0.3 * sin(t * 2.0 + float(i) * PHI);
        d = min(d, abs(sdCircle(p - pts[i], r * 0.12 * pulse)) - thick * 0.4);
    }
    for (int i = 0; i < 13; i++)
        for (int j = i + 1; j < 13; j++)
            d = min(d, sdSegment(p, pts[i], pts[j]) - thick * 0.2);
    return d;
}

// ── Golden Spiral ──────────────────────────────────────────
float goldenSpiral(vec2 p, float t) {
    float d = 1e9;
    float r = length(p);
    float a = atan(p.y, p.x);
    float b = log(PHI) / (PI * 0.5);
    float expand = 0.02 + 0.005 * sin(t * 0.3);
    for (int n = -10; n < 10; n++) {
        float theta = a + float(n) * TAU;
        d = min(d, abs(r - expand * exp(b * (theta + t * 0.5))));
    }
    return d;
}

// ── Sri Yantra (pulsing triangles) ─────────────────────────
float sdTri(vec2 p, float s, float a) {
    p = rot(a) * p;
    vec2 q = abs(p);
    return max(q.x * 0.866 + p.y * 0.5, -p.y) - s * 0.5;
}

float sriYantra(vec2 p, float size, float thick, float t) {
    float d = 1e9;
    for (int i = 0; i < 4; i++) {
        float pulse = 1.0 + 0.04 * sin(t * 0.7 + float(i) * 0.8);
        float s = size * (1.0 - float(i) * 0.2) * pulse;
        d = min(d, abs(sdTri(p - vec2(0, -float(i) * size * 0.06), s, 0.0)) - thick);
    }
    for (int i = 0; i < 5; i++) {
        float pulse = 1.0 + 0.04 * sin(t * 0.6 + float(i) * 0.6 + PI);
        float s = size * (0.95 - float(i) * 0.18) * pulse;
        d = min(d, abs(sdTri(p + vec2(0, -float(i) * size * 0.055), s, PI)) - thick);
    }
    d = min(d, abs(sdCircle(p, size * 0.58)) - thick);
    return d;
}

// ── Vesica Piscis (breathing) ──────────────────────────────
float vesicaPiscis(vec2 p, float r, float thick, float t) {
    float sep = 0.5 + 0.15 * sin(t * 0.4);
    float d1 = sdCircle(p - vec2(-r * sep, 0.0), r);
    float d2 = sdCircle(p - vec2( r * sep, 0.0), r);
    return min(min(abs(d1), abs(d2)) - thick, abs(max(d1, d2)) - thick * 0.5);
}

// ── Mandala kaleidoscope (polar mirror symmetry) ───────────
float mandala(vec2 p, float t) {
    float r = length(p);
    float a = atan(p.y, p.x);
    // 12-fold mirror symmetry
    float sectors = 12.0;
    a = abs(mod(a, TAU / sectors) - PI / sectors);
    vec2 mp = r * vec2(cos(a), sin(a));

    float d = 1e9;
    // Nested petal arcs
    for (int i = 1; i <= 5; i++) {
        float ri = float(i) * 0.09;
        float wave = 0.01 * sin(t * 0.8 + float(i) * 1.3);
        d = min(d, abs(sdCircle(mp - vec2(ri + wave, 0.0), 0.06 + float(i) * 0.008)));
    }
    // Dot accents on rings
    for (int i = 1; i <= 4; i++) {
        float ri = float(i) * 0.11;
        float dotR = 0.008 + 0.004 * sin(t * 1.5 + float(i));
        d = min(d, sdCircle(mp - vec2(ri, 0.0), dotR));
    }
    return d;
}

// ── Aurora ribbons ─────────────────────────────────────────
float aurora(vec2 p, float t) {
    float v = 0.0;
    for (int i = 0; i < 5; i++) {
        float fi = float(i);
        float freq = 2.5 + fi * 0.8;
        float speed = 0.2 + fi * 0.05;
        float amp = 0.04 / (1.0 + fi * 0.5);
        float yOff = (fi - 2.0) * 0.08;
        float wave = amp * sin(p.x * freq + t * speed + fi * 1.5)
                   + amp * 0.5 * sin(p.x * freq * 2.3 + t * speed * 1.7 + fi);
        float ribbon = exp(-pow((p.y - yOff - wave) * 15.0, 2.0));
        // Shimmer
        ribbon *= 0.7 + 0.3 * sin(p.x * 20.0 + t * 3.0 + fi * 2.0);
        v += ribbon;
    }
    return v;
}

// ── God rays (volumetric light beams from center) ──────────
float godRays(vec2 p, float t) {
    float a = atan(p.y, p.x);
    float r = length(p);
    float rays = 0.0;
    // 7 beams (sacred number) slowly rotating
    for (int i = 0; i < 7; i++) {
        float beamAngle = float(i) * TAU / 7.0 + t * 0.07;
        float diff = abs(mod(a - beamAngle + PI, TAU) - PI);
        float beam = exp(-diff * 12.0);
        // Fade with distance and pulse
        float pulse = 0.6 + 0.4 * sin(t * 0.5 + float(i) * 0.9);
        beam *= exp(-r * 1.5) * pulse;
        // Animated dust in beam
        float dust = noise(vec2(a * 5.0 + float(i), r * 8.0 - t * 0.5));
        beam *= 0.7 + 0.3 * dust;
        rays += beam;
    }
    return rays;
}

// ── Sparkle field ──────────────────────────────────────────
float sparkles(vec2 uv, float t) {
    float s = 0.0;
    for (int i = 0; i < 40; i++) {
        float fi = float(i);
        float angle = fi * PHI * TAU + t * (0.08 + fi * 0.003);
        float radius = 0.08 + 0.4 * hash11(fi * 0.73);
        vec2 pos = radius * vec2(cos(angle + fi), sin(angle * 0.7 + fi * 1.3));
        float twinkle = pow(0.5 + 0.5 * sin(t * 4.0 + fi * 3.7), 4.0);
        s += twinkle * exp(-length(uv - pos) * 300.0);
    }
    return s;
}

// ── Energy ripples ─────────────────────────────────────────
float energyRipple(vec2 p, float t) {
    float r = length(p);
    float v = 0.0;
    for (int i = 0; i < 4; i++) {
        float phase = float(i) * 1.8;
        float speed = 2.0 + float(i) * 0.3;
        float envelope = exp(-r * 2.5) * exp(-pow(mod(t * 0.35 + phase * 0.25, 3.5) - 1.5, 2.0) * 1.5);
        v += sin(r * 30.0 - t * speed + phase) * envelope;
    }
    return v;
}

// ════════════════════════════════════════════════════════════
void main()
{
    vec2 res = uTDOutputInfo.res.zw;
    vec2 uv = vUV.st - 0.5;
    uv.x *= res.x / res.y;
    float t = u_time;

    // ── Domain warp the entire scene subtly ──────────────────
    vec2 warp = domainWarp(uv * 2.5, t);
    vec2 uvW = uv + warp * 0.06; // subtle organic flow

    // Scene phasing (60s full cycle)
    float cycle = t * 0.105;
    float ph0 = smoothstep(0.0, 0.35, 0.5 + 0.5 * sin(cycle));
    float ph1 = smoothstep(0.0, 0.35, 0.5 + 0.5 * sin(cycle - 1.2));
    float ph2 = smoothstep(0.0, 0.35, 0.5 + 0.5 * sin(cycle - 2.4));
    float ph3 = smoothstep(0.0, 0.35, 0.5 + 0.5 * sin(cycle - 3.6));
    float ph4 = smoothstep(0.0, 0.35, 0.5 + 0.5 * sin(cycle - 4.8));

    float rotA = t * 0.04 + 0.03 * sin(t * 0.11);
    vec2 uvR = rot(rotA) * uvW;
    float zoom = 1.0 + 0.06 * sin(t * 0.17);
    vec2 uvZ = uvW * zoom;
    vec2 uvRZ = uvR * zoom;

    // ── 1. Deep nebula background ────────────────────────────
    float n1 = fbm(uv * 2.5 + vec2(t * 0.04, t * 0.03));
    float n2 = fbm(uv * 3.0 + vec2(-t * 0.03, t * 0.05) + 5.0);
    vec3 bg = mix(pal[8], pal[0], n1 * 0.8);
    bg = mix(bg, pal[7], n2 * 0.4);
    bg = mix(bg, pal[9], smoothstep(0.4, 0.7, length(uv)) * 0.5);

    // ── 2. God rays ──────────────────────────────────────────
    float rays = godRays(uvR, t);
    vec3 rayCol = mix(pal[1], pal[5], 0.5 + 0.5 * sin(t * 0.2));
    bg += rayCol * rays * 0.35;

    // ── 3. Aurora ribbons ────────────────────────────────────
    vec2 auroraUV = rot(t * 0.02) * uv;
    float aur = aurora(auroraUV * 2.0, t);
    vec3 aurCol = mix(pal[2], pal[5], 0.5 + 0.5 * sin(auroraUV.x * 3.0 + t * 0.4));
    aurCol = mix(aurCol, pal[4], 0.3);

    // ── 4. Quasicrystal interference ─────────────────────────
    float pent = 1e9;
    vec2 pentP = uvRZ * 3.0;
    for (int i = 0; i < 5; i++) {
        float a = float(i) * TAU / 5.0 + t * 0.1;
        pent = min(pent, 0.5 + 0.5 * cos(dot(pentP, vec2(cos(a), sin(a))) * TAU + t * 0.3));
    }
    vec3 pentCol = getColor(pent * 0.5 + t * 0.02);

    // ── 5. Flower of Life ────────────────────────────────────
    float fol = flowerOfLife(uvRZ * 2.5, 0.35, 0.005, t);
    float folG = exp(-abs(fol) * 50.0) * ph0;
    vec3 folC = accentColor(length(uvR) * 2.0 + atan(uvR.y, uvR.x) * 0.3, t);

    // ── 6. Golden Spiral ─────────────────────────────────────
    vec2 spUV = rot(-t * 0.08) * uvZ;
    float sp = goldenSpiral(spUV, t * 0.2);
    float spG = exp(-sp * 70.0) * 0.8 * ph1;
    vec3 spC = mix(pal[4], pal[6], 0.5 + 0.5 * cos(t * 0.4 + length(uv) * 4.0));

    // ── 7. Sri Yantra ────────────────────────────────────────
    vec2 sriUV = rot(t * 0.03) * uvZ;
    float sri = sriYantra(sriUV, 0.7, 0.004, t);
    float sriG = exp(-abs(sri) * 55.0) * 0.65 * ph2;
    vec3 sriC = mix(pal[1], pal[3], 0.5 + 0.5 * sin(t * 0.25 + length(uv) * 5.0));

    // ── 8. Metatron's Cube ───────────────────────────────────
    vec2 metUV = rot(-t * 0.04) * uvZ;
    float met = metatronsCube(metUV, 0.25, 0.003, t);
    float metG = exp(-abs(met) * 50.0) * 0.55 * ph3;
    vec3 metC = mix(pal[5], pal[6], 0.5 + 0.5 * sin(t * 0.2 + atan(uv.y, uv.x) * 2.0));

    // ── 9. Vesica Piscis ─────────────────────────────────────
    vec2 vpUV = rot(t * 0.06 + 0.78) * uvZ;
    float vp = vesicaPiscis(vpUV, 0.4, 0.004, t);
    float vpG = exp(-abs(vp) * 55.0) * 0.45 * ph4;
    vec3 vpC = mix(pal[3], pal[5], 0.5 + 0.5 * sin(t * 0.35 + length(uv) * 6.0));

    // ── 10. Mandala kaleidoscope ─────────────────────────────
    vec2 manUV = rot(t * 0.025) * uvW;
    float man = mandala(manUV, t);
    float manG = exp(-abs(man) * 70.0) * 0.5;
    vec3 manC = accentColor(atan(manUV.y, manUV.x) * 0.5 + length(manUV) * 3.0, t);

    // ── 11. Energy ripples ───────────────────────────────────
    float rip = energyRipple(uv, t);

    // ── 12. Sparkles ─────────────────────────────────────────
    float spark = sparkles(uv, t);

    // ════ COMPOSITE ══════════════════════════════════════════
    vec3 col = bg;
    col += aurCol * aur * 0.3;
    col = mix(col, pentCol, smoothstep(0.4, 0.0, pent) * 0.15);
    col += folC * folG * 0.85;
    col += spC * spG;
    col += sriC * sriG;
    col += metC * metG;
    col += vpC * vpG;
    col += manC * manG * 0.7;
    col += mix(pal[2], pal[5], 0.5 + 0.5 * rip) * abs(rip) * 0.12;
    col += mix(pal[4], vec3(1.0), 0.5) * spark * 0.9;

    // Edge resonance across all geometry
    float edgeD = min(min(abs(fol), abs(sri)), min(abs(met), min(abs(vp), abs(man))));
    float edgeP = exp(-edgeD * 90.0) * 0.12 * (0.5 + 0.5 * sin(t * 1.8));
    col += pal[4] * edgeP;

    // ── Chromatic aberration bloom ───────────────────────────
    // Shift geometry glow per channel for prismatic edges
    float chromStr = 0.003 * (0.5 + 0.5 * sin(t * 0.3));
    vec2 uvCr = uv + normalize(uv + 0.001) * chromStr;
    vec2 uvCb = uv - normalize(uv + 0.001) * chromStr;
    float folCr = flowerOfLife(rot(rotA) * uvCr * zoom * 2.5, 0.35, 0.005, t);
    float folCb = flowerOfLife(rot(rotA) * uvCb * zoom * 2.5, 0.35, 0.005, t);
    col.r += exp(-abs(folCr) * 50.0) * ph0 * 0.08;
    col.b += exp(-abs(folCb) * 50.0) * ph0 * 0.08;

    // ── Animated vignette ────────────────────────────────────
    float vigS = 1.0 + 0.15 * sin(t * 0.15);
    col *= clamp(1.0 - dot(uv, uv) * vigS, 0.0, 1.0);

    // ── Film grain ───────────────────────────────────────────
    col += (hash21(uv * 600.0 + fract(t * 7.0)) - 0.5) * 0.02;

    // ── Tone mapping & color grade ───────────────────────────
    col = col / (1.0 + col);                   // Reinhard
    col = pow(col, vec3(0.88, 0.90, 0.92));    // Slight warm-cool split
    col = mix(col, col * col * (3.0 - 2.0 * col), 0.15); // Contrast S-curve

    fragColor = TDOutputSwizzle(vec4(col, 1.0));
}