Back to shaders

Shader test bench

20250122

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;
// "Beautiful Flower of Life" in a spiritual, mandala-like style.
// 
// Uniforms expected:
//   - u_time (float): Time in seconds
//   - u_resolution (vec2): The width and height of the viewport
//
// The final color is written to fragColor.

uniform float u_time;
uniform vec2 u_resolution;

#define fragColor gl_FragColor

// ---------------------------------------------------
// 1) Simple pseudo-random noise (hash-based)
// ---------------------------------------------------
float hash21(vec2 p)
{
    // A quick, hacky hash function
    p = fract(p * vec2(234.34, 435.345));
    p += dot(p, p + 34.345);
    return fract(p.x * p.y);
}

float noise2D(vec2 p)
{
    vec2 i = floor(p);
    vec2 f = fract(p);

    // 4 corners in 2D
    float a = hash21(i);
    float b = hash21(i + vec2(1.0, 0.0));
    float c = hash21(i + vec2(0.0, 1.0));
    float d = hash21(i + vec2(1.0, 1.0));

    // Smooth fade
    vec2 u = f * f * (3.0 - 2.0 * f);

    // Bilinear blend
    return mix(
        mix(a, b, u.x),
        mix(c, d, u.x),
        u.y
    );
}

// ---------------------------------------------------
// 2) Hue rotation utility (HSV-like)
// ---------------------------------------------------
vec3 hueColor(float h)
{
    // Wrap hue to [0,1)
    h = fract(h);

    float r = abs(h * 6.0 - 3.0) - 1.0;
    float g = 2.0 - abs(h * 6.0 - 2.0);
    float b = 2.0 - abs(h * 6.0 - 4.0);

    return clamp(vec3(r, g, b), 0.0, 1.0);
}

// ---------------------------------------------------
// 3) Function to add thin ring coverage around circle boundary
//    - Returns how much this circle contributes (for overlap summation)
// ---------------------------------------------------
float circleRing(vec2 pos, vec2 center, float radius, float thickness)
{
    // distance from ring boundary
    float distCircle = abs(length(pos - center) - radius);
    return smoothstep(0.0, thickness, thickness - distCircle);
}

// ---------------------------------------------------
// 4) Standard Flower of Life arrangement (7 circles)
//    - Center circle + 6 around at distance 'R'
//    - Each circle is radius 'R', so they intersect
//      at each other's centers in classic FOL geometry.
//    - Returns a "coverage" value where the rings form
//      thin boundaries (summed overlap).
// ---------------------------------------------------
float flowerOfLife(vec2 uvSwirl)
{
    // Circle radius for each ring
    float R = 0.35;
    // Thickness of each ring boundary
    float ringThickness = 0.015;

    float coverage = 0.0;

    // 1) Center circle (at 0,0)
    coverage += circleRing(uvSwirl, vec2(0.0), R, ringThickness);

    // 2) The 6 surrounding circles
    for(int i = 0; i < 6; i++)
    {
        float angle = 2.0 * 3.14159 * float(i) / 6.0;
        vec2 c = vec2(cos(angle), sin(angle)) * R;
        coverage += circleRing(uvSwirl, c, R, ringThickness);
    }

    // --- Uncomment if you want a larger pattern (2nd ring around) ---
    // for(int i = 0; i < 12; i++)
    // {
    //     float angle = 2.0 * 3.14159 * float(i) / 12.0;
    //     // centers at 2R for the second ring
    //     vec2 c = vec2(cos(angle), sin(angle)) * (2.0 * R);
    //     coverage += circleRing(uvSwirl, c, R, ringThickness);
    // }

    return coverage;
}

// ---------------------------------------------------
// 5) Main
// ---------------------------------------------------
void main()
{
    // Normalize coords to [-0.5*aspect, 0.5*aspect] x [-0.5, 0.5]
    vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution) / u_resolution.y;
    float lenUV   = length(uv);
    float angleUV = atan(uv.y, uv.x);

    // -------------------------
    // Base swirl in background
    // -------------------------
    float swirlStrength = 0.5;
    float swirl = swirlStrength * sin(lenUV * 3.0 - u_time * 0.4);
    float ca    = cos(swirl);
    float sa    = sin(swirl);

    // Apply swirl rotation to uv
    vec2 uvSwirl;
    uvSwirl.x = uv.x * ca - uv.y * sa;
    uvSwirl.y = uv.x * sa + uv.y * ca;

    // Smooth radial fade from center
    float radialGradient = smoothstep(0.3, 1.2, lenUV);

    // -------------------------
    // Flower of Life coverage
    // -------------------------
    float folCoverage = flowerOfLife(uvSwirl);

    // -------------------------
    // Chakra-like color for interior
    // (Shift hue over time, plus angle & distance mods)
    // -------------------------
    float baseHue   = 0.55 + 0.1 * sin(u_time * 0.3);  // overall hue shift
    float angleShift = 0.1  * sin(angleUV * 6.0);
    float distShift  = 0.15 * sin(lenUV * 3.0 - u_time * 0.8);

    float finalHue   = baseHue + angleShift + distShift;
    vec3 spiritualColor = hueColor(finalHue);

    // Combine color with coverage from the FOL geometry & radial fade
    // (We invert radialGradient so that the pattern is strong near the center)
    vec3 color = spiritualColor * folCoverage * (1.0 - radialGradient);

    // -------------------------
    // Ambient swirl & "prana" (noise)
    // -------------------------
    float noiseScale = 2.5;
    float n = noise2D(uvSwirl * noiseScale + u_time * 0.2);
    // Soft aura around everything
    float aura = 0.2 * n * (1.0 - radialGradient);
    color += aura * vec3(0.7, 0.9, 1.0);

    // -------------------------
    // Pulsating energy rings
    // -------------------------
    float energyRings = 0.5 * sin(lenUV * 25.0 - u_time * 3.0) + 0.5;
    energyRings       *= smoothstep(0.1, 0.45, lenUV) * 0.7;

    // Shift ring color slightly
    vec3 ringColor = hueColor(baseHue + 0.05) * energyRings;
    color += ringColor;

    // -------------------------
    // Soft cosmic glow
    // -------------------------
    float glowStrength = 0.16 / (lenUV + 0.12);
    color += glowStrength * vec3(1.2, 1.0, 0.8);

    // -------------------------
    // Gentle sparkles
    // -------------------------
    float sparkles = sin(lenUV * 60.0 - u_time * 8.0)
                   * cos(lenUV * 40.0 + u_time * 7.0) * 0.03;
    color += vec3(sparkles * 1.2, sparkles * 0.8, sparkles * 1.0);

    // -------------------------
    // Final color adjustments
    // -------------------------
    // Mild gamma correction
    color = pow(color, vec3(1.3));
    // Slight brightness boost
    color *= 1.6;

    fragColor = vec4(color, 1.0);
}