Back to shaders

Shader test bench

20260426

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)
// ============================================================
// SACRED MANDALA — layered geometry, single living composition
// TouchDesigner GLSL TOP — 20260426
// Required uniforms (Vectors tab):
//   iTime  (float, 1x1)  ← Timer CHOP
// Optional (declare in TD as separate float uniforms; default 0 if unbound):
//   iWarp  (float, 1x1)  ← 0..1 extra kaleido boost over baseline 0.55
//   iBeat  (float, 1x1)  ← 0..1 pulse input (zoom + chromatic flash)
// ============================================================

uniform float iTime;
uniform float iWarp;
uniform float iBeat;

layout(location = 0) #define fragColor gl_FragColor

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

// ── Palette ────────────────────────────────────────────────
const vec3 cBg1    = vec3(0.039, 0.059, 0.051); // deep forest night
const vec3 cBg2    = vec3(0.051, 0.129, 0.216); // ocean midnight
const vec3 cBg3    = vec3(0.102, 0.102, 0.180); // deep violet dusk
const vec3 cGreen  = vec3(0.000, 1.000, 0.529); // electric green
const vec3 cCyan   = vec3(0.000, 0.831, 1.000); // neon cyan
const vec3 cPurple = vec3(0.482, 0.184, 1.000); // electric purple
const vec3 cPink   = vec3(1.000, 0.176, 0.478); // hot pink
const vec3 cMint   = vec3(0.690, 1.000, 0.910); // jade mist
const vec3 cIce    = vec3(0.878, 0.969, 1.000); // ice halo
const vec3 cWhite  = vec3(1.000, 1.000, 1.000);

// ── 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(127.619, 311.713));
    p += dot(p, p + 47.31);
    return fract(p.x * p.y);
}

// Cosine palette (Inigo Quilez) — tuned for cyan/green/purple range
vec3 iqPal(float t) {
    vec3 a = vec3(0.50, 0.55, 0.55);
    vec3 b = vec3(0.45, 0.40, 0.45);
    vec3 c = vec3(1.00, 1.00, 1.00);
    vec3 d = vec3(0.50, 0.20, 0.67);
    return a + b * cos(TAU * (c * t + d));
}

// Kaleidoscope folding around origin
vec2 kaleido(vec2 p, float segs) {
    float a = atan(p.y, p.x);
    float r = length(p);
    float slice = TAU / segs;
    a = mod(a + slice * 0.5, slice) - slice * 0.5;
    return vec2(cos(a), sin(a)) * r;
}

// SDFs
float sdCircle(vec2 p, float r)              { return length(p) - r; }
float sdRing(vec2 p, float r)                { return abs(length(p) - r); }
float sdSeg(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);
}
float sdEqTri(vec2 p, float r) {
    const float k = SQRT3;
    p.x = abs(p.x) - r;
    p.y = p.y + r / k;
    if (p.x + k * p.y > 0.0) p = vec2(p.x - k * p.y, -k * p.x - p.y) * 0.5;
    p.x -= clamp(p.x, -2.0 * r, 0.0);
    return -length(p) * sign(p.y);
}

// Soft glow shaping
float glow(float d, float w, float power) {
    return pow(w / max(abs(d), 1e-4), power);
}
float band(float d, float halfW, float aa) {
    return smoothstep(halfW + aa, halfW - aa, abs(d));
}

// ── Layer 1: Flower of Life (inner heart, breathing) ───────
float flowerLayer(vec2 p, float t) {
    float r = 0.18 + 0.012 * sin(t * 0.9);
    float d = sdRing(p, r);
    for (int i = 0; i < 6; i++) {
        float a = float(i) * TAU / 6.0 + t * 0.08;
        d = min(d, sdRing(p - vec2(cos(a), sin(a)) * r, r));
    }
    for (int i = 0; i < 6; i++) {
        float a = float(i) * TAU / 6.0 + PI / 6.0 - t * 0.05;
        d = min(d, sdRing(p - vec2(cos(a), sin(a)) * r * SQRT3, r));
    }
    return d;
}

// ── Layer 2: Metatron lattice (rotating, mid-ring) ─────────
float metatronLayer(vec2 p, float t) {
    p *= rot(t * 0.07);
    float ringR = 0.46;
    float d = sdRing(p, ringR);
    vec2 nodes[7];
    nodes[0] = vec2(0.0);
    for (int i = 0; i < 6; i++) {
        float a = float(i) * TAU / 6.0;
        nodes[i + 1] = vec2(cos(a), sin(a)) * ringR;
    }
    // node dots
    for (int i = 0; i < 7; i++) {
        d = min(d, sdCircle(p - nodes[i], 0.018));
    }
    // chords between hex nodes (1..6)
    for (int i = 1; i < 7; i++) {
        for (int j = i + 1; j < 7; j++) {
            d = min(d, sdSeg(p, nodes[i], nodes[j]) - 0.0015);
        }
    }
    return d;
}

// ── Layer 3: Sri Yantra triangles (counter-rotating) ───────
float yantraLayer(vec2 p, float t) {
    p *= rot(-t * 0.05);
    float d = 1e5;
    for (int i = 0; i < 4; i++) {
        float fi = float(i);
        float scl = 0.62 - fi * 0.07;
        // Triangle pointing up
        float dUp = abs(sdEqTri(p, scl));
        // Triangle pointing down (rotated 180)
        vec2 q = -p;
        float dDn = abs(sdEqTri(q, scl * 0.92));
        d = min(d, dUp);
        d = min(d, dDn);
    }
    return d;
}

// ── Layer 4: 12-fold lotus petals (outer corona) ───────────
float lotusLayer(vec2 p, float t) {
    p *= rot(t * 0.04);
    float a = atan(p.y, p.x);
    float r = length(p);
    float k = 12.0;
    float petals = 0.74 + 0.06 * sin(t * 0.6);
    float shape = petals * (0.55 + 0.45 * cos(k * a));
    return abs(r - shape);
}

// ── Layer 5: Fibonacci spiral dust (sparkle backdrop) ──────
float fibonacciDust(vec2 p, float t, out float spark) {
    float d = 1e5;
    spark = 0.0;
    float phi = 137.508 * PI / 180.0;
    for (int i = 0; i < 64; i++) {
        float fi = float(i);
        float a = fi * phi + t * 0.18;
        float r = sqrt(fi) * 0.085;
        vec2 c = vec2(cos(a), sin(a)) * r;
        float di = sdCircle(p - c, 0.006 + fi * 0.00018);
        d = min(d, di);
        spark += smoothstep(0.014, 0.0, length(p - c)) * (0.5 + 0.5 * sin(t * 1.7 + fi));
    }
    return d;
}

void main() {
    vec2 uv = vUV.st;
    vec2 st = uv * 2.0 - 1.0;
    st.x   *= uTDOutputInfo.res.z / uTDOutputInfo.res.w;

    float t = iTime;

    // Subtle global breath / beat zoom
    float breath = 1.0 + 0.025 * sin(t * 0.5);
    float beatK  = clamp(iBeat, 0.0, 1.0);
    st /= breath;
    st *= 1.0 - 0.06 * beatK;

    // Slow whole-canvas rotation
    st *= rot(t * 0.025);

    // Kaleidoscopic warp — baseline 1.0 (full 12-fold symmetry), iWarp has no effect at baseline
    float warpAmt = clamp(1.0 + iWarp * 0.0, 0.0, 1.0);
    vec2 stK = kaleido(st, 12.0);
    vec2 stW = mix(st, stK, warpAmt);

    // ── Compute SDF layers ───────────────────────────────
    float dFlower   = flowerLayer(stW, t);
    float dMetatron = metatronLayer(stW, t);
    float dYantra   = yantraLayer(stW, t);
    float dLotus    = lotusLayer(stW, t);
    float dustSpark;
    float dDust     = fibonacciDust(stW, t, dustSpark);

    // ── Background: deep teal-ocean gradient ─────────────
    float r2  = dot(st, st);
    float ang = atan(st.y, st.x);
    vec3 bg   = mix(cBg1, cBg2, smoothstep(0.0, 0.6, r2));
    bg        = mix(bg, cBg3, 0.35 * smoothstep(0.2, 1.4, r2));
    // soft angular wash
    float wash = 0.5 + 0.5 * sin(ang * 3.0 + t * 0.2);
    bg += iqPal(wash * 0.5 + t * 0.04) * 0.06;

    // Hue cycling along radius — iridescent feel
    float hueT = length(st) * 0.7 + t * 0.05;
    vec3 iri   = iqPal(hueT);

    vec3 col = bg;

    // Fibonacci dust — soft glow + crisp dot
    col += iqPal(t * 0.07 + 0.20) * glow(dDust, 0.004, 1.1) * 0.55;
    col += vec3(1.0) * smoothstep(0.0035, 0.0, dDust) * 0.9;
    col += cIce * dustSpark * 0.06;

    // Lotus corona — pink/cyan pulsing halo
    float lotusGlow = glow(dLotus, 0.018, 0.85);
    col += mix(cPink, cCyan, 0.5 + 0.5 * sin(t * 0.3)) * lotusGlow * 0.45;
    col += cWhite * band(dLotus, 0.0028, 0.0018) * 0.9;

    // Sri Yantra — purple ritual lines
    float yantraGlow = glow(dYantra, 0.012, 0.95);
    col += mix(cPurple, cBg3 * 4.0, 0.4) * yantraGlow * 0.55;
    col += cIce * band(dYantra, 0.0022, 0.0016) * 0.9;

    // Metatron lattice — cyan/mint crystal threads
    float metaGlow = glow(dMetatron, 0.010, 1.0);
    col += mix(cCyan, cMint, 0.35) * metaGlow * 0.7;
    col += cIce * band(dMetatron, 0.0020, 0.0014) * 1.1;

    // Flower of Life — electric green core (brighter, central focus)
    float flowerGlow = glow(dFlower, 0.018, 1.2);
    col += mix(cGreen, cIce, 0.25) * flowerGlow * 1.3;
    col += cWhite * band(dFlower, 0.0026, 0.0016) * 1.6;

    // Iridescent radial wash modulating overall hue
    col = mix(col, col * (0.6 + 0.6 * iri), 0.35);

    // Center bloom — cyan-ice focal light (wider, stronger)
    float centerBloom = exp(-r2 * 4.0);
    col += mix(cCyan, cIce, 0.55) * centerBloom * (0.65 + 0.30 * sin(t * 0.4));

    // Outer petal aura — chromatic ring at r ~ 0.95
    float aura = exp(-pow(length(st) - 0.95, 2.0) * 32.0);
    col += iqPal(ang / TAU + t * 0.1) * aura * 0.25;

    // 12-fold star sparkle — kaleidoscopic micro-glints
    vec2 starP = kaleido(st * 1.6, 12.0);
    float starN = hash21(floor(starP * 60.0) + floor(t * 2.0));
    float twinkle = step(0.985, starN) * (0.5 + 0.5 * sin(t * 4.0 + starN * 30.0));
    col += cIce * twinkle * 0.55;

    // Beat reactive pulse on whole frame
    col += iri * beatK * 0.10;

    // Soft intro fade (first 2 seconds)
    col *= smoothstep(0.0, 2.0, t);

    // Vignette using deep bg
    float vig = smoothstep(1.45, 0.25, length(st));
    col = mix(cBg1 * 0.4, col, vig);

    // Tone map + gentle gamma for richness
    col = col / (col + 0.85);
    col = pow(max(col, 0.0), vec3(0.92));

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