Candy Interference

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

Math pretending to be a Lisa Frank poster. Iridescent chaos, procedurally generated.

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)

 // ============================================================
//  LISA FRANK FIELD v1.0
//  Controls: glitchAmt, warpScale, iridescence, 
//            aberration, speed, brightness
// ============================================================

#define PI 6.28318530718

// ---- KNOBS ---- (tweak these first) ----
float glitchAmt    = 0.35;   // scan-line / jitter strength
float warpScale    = 3.2;    // how wild the domain warp gets
float iridescence  = 1.8;    // color band density
float aberration   = 0.18;  // chromatic split amount
float speed        = 0.4;    // animation speed
float brightness   = 1.3;    // final output gain

// ---- PALETTE: Lisa Frank cosine ----
// Cycles: hot pink → electric cyan → lemon → violet → repeat
vec3 lfPalette(float t) {
    vec3 a = vec3(0.7, 0.4, 0.7);   // bias  (pink-violet base)
    vec3 b = vec3(0.6, 0.5, 0.5);   // amplitude
    vec3 c = vec3(1.0, 1.2, 0.8);   // frequency per channel
    vec3 d = vec3(0.0, 0.33, 0.67); // phase offset (spreads hues)
    return a + b * cos(PI * (c * t + d));
}

// ---- Hot neon accent palette ----
vec3 acidPalette(float t) {
    vec3 a = vec3(0.5, 0.5, 0.5);
    vec3 b = vec3(0.5, 0.5, 0.5);
    vec3 c = vec3(2.0, 1.0, 0.0);
    vec3 d = vec3(0.5, 0.2, 0.25);
    return a + b * cos(PI * (c * t + d));
}

// ---- Hash / noise ----
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 noise(vec2 p) {
    vec2 i = floor(p);
    vec2 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);
}

// ---- FBM domain warp (2 octaves is cheap & plenty weird) ----
vec2 domainWarp(vec2 p, float t) {
    float n1 = noise(p * 1.5 + t * 0.3);
    float n2 = noise(p * 2.3 - t * 0.2 + vec2(5.2, 1.3));
    vec2 warp1 = vec2(n1, n2) * 2.0 - 1.0;
    
    float n3 = noise((p + warp1 * 0.8) * 2.0 + t * 0.15);
    float n4 = noise((p + warp1 * 0.8) * 1.7 - t * 0.25 + vec2(1.7, 9.2));
    vec2 warp2 = vec2(n3, n4) * 2.0 - 1.0;
    
    return p + (warp1 * 0.6 + warp2 * 0.4) * warpScale * 0.25;
}

// ---- Thin-film interference field ----
float thinFilm(vec2 p, float t) {
    float r = length(p);
    float a = atan(p.y, p.x);
    // Concentric interference rings warped by angle
    return sin(r * iridescence * 8.0 - t * 2.0) 
         * sin(a * 3.0 + r * 4.0 + t)
         + sin(r * iridescence * 13.0 + a * 2.0 - t * 1.3);
}

// ---- CRT scanline grime ----
float scanlines(vec2 uv, float t) {
    float line  = sin(uv.y * 200.0 + t * 10.0) * 0.5 + 0.5;
    float coarse = sin(uv.y * 40.0 - t * 3.0) * 0.5 + 0.5;
    return mix(1.0, 0.75 + line * 0.25 * coarse, glitchAmt * 0.4);
}

// ---- H-jitter glitch (VHS horizontal displacement) ----
vec2 vhsJitter(vec2 uv, float t) {
    float band = floor(uv.y * 15.0 + t * 7.0);
    float jitter = (hash(vec2(band, floor(t * 20.0))) - 0.5) 
                    * glitchAmt * 0.04;
    return uv + vec2(jitter, 0.0);
}

// ---- Main ----
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
    float t = iTime * speed;
    
    // Apply VHS jitter
    vec2 jUV = vhsJitter(uv, t);
    
    // Domain warp the coordinate space
    vec2 wp  = domainWarp(jUV * warpScale, t);
    
    // Chromatic aberration: sample warped field at 3 slightly offset UVs
    float filmR = thinFilm(domainWarp(jUV * warpScale + aberration, t), t);
    float filmG = thinFilm(wp, t);
    float filmB = thinFilm(domainWarp(jUV * warpScale - aberration * 1.3, t), t);
    
    // Map film values to palette phase
    float phaseR = filmR * 0.5 + 0.5;
    float phaseG = filmG * 0.5 + 0.5;
    float phaseB = filmB * 0.5 + 0.5;
    
    // Sample two palettes and blend
    vec3 colA = lfPalette(phaseG + t * 0.1);
    vec3 colB = acidPalette(phaseG * 0.7 + length(wp) * 0.2 - t * 0.08);
    
    // Combine channels with aberration split
    vec3 col;
    col.r = mix(colA.r, colB.r, phaseR) * 1.1;
    col.g = mix(colA.g, colB.g, phaseG);
    col.b = mix(colA.b, colB.b, phaseB) * 1.15;
    
    // Glow accumulator — adds neon plasma rim light
    float glowField = abs(sin(filmG * 3.0 + t)) / (0.1 + abs(filmG));
    glowField = clamp(glowField * 0.04, 0.0, 0.8);
    col += lfPalette(length(uv) + t * 0.2) * glowField;
    
    // Scanlines
    col *= scanlines(jUV, t);
    
    // Vignette (soft, barely there)
    col *= 1.0 - dot(uv, uv) * 0.3;
    
    // Output
    col = pow(col * brightness, vec3(0.88)); // gamma + boost
    col = clamp(col, 0.0, 1.0);
    
    fragColor = vec4(col, 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