PRISMACORE_003 — Retina Banquet

GLSL shader by merrypranxter · created 2026-02-21 · 10s loop · 2 passes

Eight-fold polar symmetry with two counter-rotating folds fighting each other for control of the geometry. Triple-pass domain warp where each layer drifts at a different speed so space never settles. R, G, and B channels warped independently through separate time offsets — chromatic aberration as temporal desync, not just spatial shift. Razor-thin sin-threshold ridges blow out additively on top of per-channel thin-film iridescence. Strobe pulse on exposure. Glitch tears wide enough to lose your footing. CRT grime cranked. ACES tonemapper fought valiantly and lost. Math that wants to hurt you, but make it Lisa Frank

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)

// PRISMACORE_003 — Retina Banquet
// 8-fold polar war + razor ridges + triple warp + strobe bloom + glitch tears
// Shadertoy: iTime, iResolution

#define PI  3.14159265
#define TAU 6.28318530

// ════════════════════════════════
// KNOBS
// ════════════════════════════════
#define FOLD_N      8.0    // symmetry arms
#define WARP_SCALE  3.2    // domain warp violence
#define RIDGE_FREQ  18.0   // razor line density (higher = more cuts)
#define RIDGE_SHARP 0.04   // ridge width (lower = thinner = more intense)
#define GLITCH_AMT  0.55   // tear probability
#define DAMAGE      0.7    // CRT grime level
#define IRIDESCENCE 2.8    // color cycle density
#define STROBE      0.18   // exposure pulse amplitude
#define FLOW_SPEED  0.6    // animation pace
// ════════════════════════════════

float hash(vec2 p) {
    p = fract(p * vec2(127.1, 311.7));
    p += dot(p, p + 19.19);
    return fract(p.x * p.y);
}
float hash1(float x) { return fract(sin(x * 127.1) * 43758.5); }
vec2  hash2(vec2 p)  { return vec2(hash(p), hash(p + 17.3)); }

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

float fbm(vec2 p, int oct) {
    float v = 0.0, a = 0.5;
    for (int i = 0; i < 6; i++) {
        if (i >= oct) break;
        v += a * vnoise(p);
        p  = p * 2.1 + vec2(1.7, 9.2);
        a *= 0.5;
    }
    return v;
}

// ---- POLAR FOLD (dual rotation) ----
vec2 polarFold(vec2 p, float t) {
    // two counter-rotating layers
    float a1 = atan(p.y, p.x) + t * FLOW_SPEED * 0.07;
    float a2 = atan(p.y, p.x) - t * FLOW_SPEED * 0.11;
    float r  = length(p);
    float seg = TAU / FOLD_N;
    float fa1 = mod(a1, seg); fa1 = min(fa1, seg - fa1);
    float fa2 = mod(a2, seg); fa2 = min(fa2, seg - fa2);
    // blend between the two folds — they phase in/out against each other
    float blend = sin(t * FLOW_SPEED * 0.3) * 0.5 + 0.5;
    float fa = mix(fa1, fa2, blend);
    return vec2(cos(fa), sin(fa)) * r;
}

// ---- TRIPLE WARP ----
vec2 warp3(vec2 p, float t) {
    float s = FLOW_SPEED;
    // pass 1
    vec2 q1 = vec2(fbm(p + vec2(t*s*0.7, 1.3), 4),
                   fbm(p + vec2(2.8, t*s*0.5), 4));
    vec2 w1 = p + q1 * (WARP_SCALE * 0.35);
    // pass 2 — offset time
    vec2 q2 = vec2(fbm(w1 + vec2(t*s*1.1 + 4.2, 0.9), 4),
                   fbm(w1 + vec2(1.3, t*s*0.8 + 3.1), 4));
    vec2 w2 = w1 + q2 * (WARP_SCALE * 0.25);
    // pass 3 — fastest, smallest, adds micro-chaos
    vec2 q3 = vec2(fbm(w2 * 2.0 + t*s*1.7, 3),
                   fbm(w2 * 2.0 + t*s*1.3 + 5.5, 3));
    return w2 + q3 * (WARP_SCALE * 0.12);
}

// ---- RAZOR RIDGES ----
// sin thresholded to knife-thin bright lines
float ridges(float field) {
    float s = sin(field * PI * RIDGE_FREQ);
    return smoothstep(1.0 - RIDGE_SHARP, 1.0, abs(s));
}

// ---- THIN FILM ----
vec3 thinFilm(float phase, float strength) {
    return vec3(
        sin(phase * TAU * strength)                * 0.5 + 0.5,
        sin(phase * TAU * strength + TAU/3.0)      * 0.5 + 0.5,
        sin(phase * TAU * strength + 2.0*TAU/3.0)  * 0.5 + 0.5
    );
}

// ---- GLITCH TEARS ----
vec2 glitch(vec2 uv, float t) {
    float band    = floor(uv.y * 24.0 + t * 9.1);
    float trigger = hash1(band + floor(t * 4.0));
    float shift   = (trigger > 1.0 - GLITCH_AMT)
                    ? (hash1(band) - 0.5) * 0.18 : 0.0;
    // occasional full-row color inversion band
    if (hash1(floor(t * 17.0) + band * 0.13) > 0.96)
        shift += (hash1(band * 5.1) - 0.5) * 0.08;
    uv.x += shift;
    return uv;
}

// ---- CRT DAMAGE ----
float crt(vec2 uv, float t) {
    float scan  = sin(uv.y * iResolution.y * 1.5) * 0.06 * DAMAGE;
    vec2  vig   = uv * 2.0 - 1.0;
    float v     = 1.0 - dot(vig, vig) * 0.45 * DAMAGE;
    float grain = (hash(uv * iResolution.xy + t * 1337.0) - 0.5) * 0.10 * DAMAGE;
    return v + scan + grain;
}

// ════════════════════════════════
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv  = fragCoord / iResolution.xy;
    float t  = iTime;
    float asp = iResolution.x / iResolution.y;

    // glitch first
    vec2 guv = glitch(uv, t);
    vec2 p   = (guv * 2.0 - 1.0) * vec2(asp, 1.0);

    // dual-rotation polar fold
    vec2 fp  = polarFold(p, t);

    // triple warp
    vec2 wp  = warp3(fp * 0.9 + 1.5, t);

    // base field
    float field = fbm(wp * 1.6, 5);

    // CHROMATIC SPLIT — each channel warped separately with time offsets
    float tR = t * 1.00;
    float tG = t * 1.05;
    float tB = t * 0.93;
    vec2 wpR = warp3(fp * 0.9 + 1.5, tR);
    vec2 wpG = warp3(fp * 0.9 + 1.7, tG);
    vec2 wpB = warp3(fp * 0.9 + 1.3, tB);
    float fR = fbm(wpR * 1.6, 5);
    float fG = fbm(wpG * 1.6, 5);
    float fB = fbm(wpB * 1.6, 5);

    // razor ridges on each channel independently
    float ridR = ridges(fR);
    float ridG = ridges(fG + 0.07); // phase offset so they don't stack perfectly
    float ridB = ridges(fB + 0.14);

    // thin film per channel
    vec3 filmR = thinFilm(fR, IRIDESCENCE);
    vec3 filmG = thinFilm(fG, IRIDESCENCE);
    vec3 filmB = thinFilm(fB, IRIDESCENCE);

    const vec3 PINK   = vec3(1.00, 0.07, 0.57);
    const vec3 CYAN   = vec3(0.00, 0.95, 1.00);
    const vec3 LIME   = vec3(0.60, 1.00, 0.00);
    const vec3 VIOLET = vec3(0.55, 0.00, 1.00);
    const vec3 WHITE  = vec3(1.00, 0.95, 1.00);

    // build color from per-channel film
    vec3 col;
    col.r = filmR.r * PINK.r   + filmR.g * LIME.r   + filmR.b * CYAN.r;
    col.g = filmG.r * PINK.g   + filmG.g * LIME.g   + filmG.b * CYAN.g;
    col.b = filmB.r * PINK.b   + filmB.g * LIME.b   + filmB.b * CYAN.b;

    // violet bleeds into depth
    float depth = 1.0 - smoothstep(0.1, 0.5, field);
    col = mix(col, VIOLET * 0.9, depth * 0.65);

    // smash razor ridges on top — additive, so they blow out
    col += vec3(ridR, ridG, ridB) * vec3(PINK.r, LIME.g, CYAN.b) * 2.5;

    // additive bloom: sample field neighbors and pile on
    float blm = 0.0;
    float br = 0.012;
    blm += ridges(fbm(warp3((fp+vec2(br,0))*0.9+1.5,t)*1.6, 4));
    blm += ridges(fbm(warp3((fp-vec2(br,0))*0.9+1.5,t)*1.6, 4));
    blm += ridges(fbm(warp3((fp+vec2(0,br))*0.9+1.5,t)*1.6, 4));
    blm += ridges(fbm(warp3((fp-vec2(0,br))*0.9+1.5,t)*1.6, 4));
    col += col * (blm / 4.0) * 1.2;

    // strobe pulse on exposure
    float strobe = 1.0 + sin(t * 13.0) * STROBE * 0.5
                       + sin(t * 7.3)  * STROBE * 0.3;
    col *= strobe;

    // CRT damage
    col *= crt(uv, t);

    // gamma + exposure push — intentionally hot
    col = pow(max(col, 0.0), vec3(0.75)) * 1.45;

    // ACES (it will try its best, bless its heart)
    col = col*(2.51*col+0.03) / (col*(2.43*col+0.59)+0.14);

    fragColor = vec4(clamp(col, 0.0, 1.0), 1.0);
}

Image

Not used

More shaders by merrypranxter

Browse all public shaders · All shaders by merrypranxter · ShaderKit home

Vibe Mode BETA
💰 ~0 credits
Uniforms
FPS: 0
Time: 0
Resolution: 0 x 0

Account

FPS: -- Time: -- --×-- 1x