Project_2026-06-24_11-11-39

GLSL shader by scry · created 2026-06-24 · 10s loop · 2 passes

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.
#define time iTime*2.*pi/10.
#define R iResolution.xy
#define ar R.x/R.y
#define M iMouse
#define xm (M.xy/R)
#define nm ((xm.xy-0.5)*vec2(ar,1.)+0.5)
mat2 r2d(float a) {
    return mat2(cos(a),sin(a),-sin(a),cos(a));
}

Buffer A (iChannel0)

// ── 04 · Wallpaper groups ───────────────────────────────────────────────────
// All 17 wallpaper groups in a 6-6-5 grid with labels.
// Click a box to go fullscreen. Click again to return to grid.
// ───────────────────────────────────────────────────────────────────────────
// State stored in pixel (0,0) of this buffer: .x = selected gid (-1 = none)

#define PI 3.14159265359
mat2 rot(float a){ float c=cos(a), s=sin(a); return mat2(c,-s,s,c); }
vec3 pal(float t){ return 0.5+0.5*cos(6.2831*(t+vec3(0.0,0.35,0.7))); }
float S(float x){ return smoothstep(0.0,1.0,x); }

vec3 motif1(vec2 q){
    float d = length(q - vec2(0.62, 0.40));
    float blob = smoothstep(0.28, 0.0, d);
    float stripe = 0.5 + 0.5*sin((q.x*3.0 + q.y*5.0)*PI*2.0 + iTime);
    float tail = smoothstep(0.10, 0.0, abs(q.y-0.7) ) * step(q.x,0.5);
    float m = blob + 0.5*stripe*blob + tail;
    return pal(0.15 + 0.5*blob + 0.2*stripe) * m;
}
vec3 motif(vec2 q) {
    vec3 col = vec3(0.);
    vec2 uv = q;
    for (int i=0;i<13;i++) {
        q += sin(q.yx*2.)*0.2;
        q.x += clamp(q.x,0.,0.1);
        
        q *= r2d(deg*25.);
    }
    col += sin(q.x*20.+vec3(1.,2.,3.));
    col *= 0.;
    uv -= 0.7;
    uv *= 4.5;
    
    uv.x = -uv.x;
    col += step(-uv.x,0.5)*step(-uv.y,0.5)*step(uv.x*2.+uv.y,0.5);
    col.rg += uv*0.1;
    col += sin(length(uv)*4.+vec3(1.,2.,3.))*0.3;
    return col;
}

vec3 hexFold(vec2 p, int mode){
    vec2 s = vec2(1.0,1.7320508);
    vec2 a = mod(p,s)-s*0.5;
    vec2 b = mod(p-s*0.5,s)-s*0.5;
    vec2 o = dot(a,a)<dot(b,b)?a:b;
    float ang = atan(o.y,o.x);
    float r = length(o);
    vec2 os = vec2(0.);
    if(mode==0){
        // p3: 3-fold rotation only
        float w = PI*2.0/3.0;
        ang = mod(ang, w);
    } else if(mode==1){
        // p3m1: 3-fold rotation + mirror through vertices
        float w = PI*2.0/3.0;
        ang = mod(ang, w);
        ang = abs(ang - w*0.5);
    } else if(mode==2){
        // p31m: 3-fold rotation + mirrors through edge midpoints (not vertices)
        // p3m1 mirrors pass through vertices (reflect at 60° = sector midpoint)
        // p31m mirrors pass through edge midpoints (offset by 30° = PI/6)
        // We rotate the whole thing by PI/6 before folding, then rotate back
        float w = PI*2.0/3.0;
        ang += PI/6.0;
        ang = mod(ang, w);
        ang = abs(ang - w*0.5);
        ang -= PI/6.0;
        os += vec2(-0.2,0.25);
    } else if(mode==3){
        // p6: 6-fold rotation only
        float w = PI/3.0;
        ang = mod(ang, w);
    } else if(mode==4){
        // p6m: 6-fold rotation + mirror
        float w = PI/3.0;
        ang = mod(ang, w);
        ang = abs(ang - w*0.5);
        ang += deg*30.;
        os += vec2(-0.1,-0.15);
    }
    vec2 q = r*vec2(cos(ang),sin(ang));
    return motif(q*1.6+0.5+os);
}

vec3 applyGroup(vec2 uv, int gid){
    vec2 p = uv * 3.0; // tile the pattern a few times inside each box
    if(gid==0){ // p1
        return motif(fract(p)+vec2(0.2,0.3));
    }
    if(gid==1){ // p2
        vec2 c=fract(p); vec2 q=c-0.5;
        float a2=atan(q.y,q.x); a2=mod(a2,PI);
        c=length(q)*vec2(cos(a2),sin(a2))+0.5;
        return motif(c);
    }
    if(gid==2){ // pm
        vec2 c=fract(p); c.y=abs(c.y-0.5)+0.5;
        return motif(c);
    }
    if(gid==3){ // pg
        vec2 c=fract(p);
        if(mod(floor(p.y),2.0)>0.5) c.x=1.-c.x;
        c.y += 0.25;
        return motif(c);
    }
    if(gid==4){ // cm
        p.y *= 2.;
        vec2 c=fract(p);
        if(mod(floor(p.x)+floor(p.y),2.0)>0.5) c=1.0-c;
        c.x=abs(c.x-0.5)+0.5;
        c.y *= 0.5;
        c.y += 0.5;
        return motif(c);
    }
    if(gid==5){ // pmm
        vec2 c=fract(p); c=abs(c-0.5)+0.5;
        return motif(c);
    }
    if(gid==6){ // pmg
        vec2 c=fract(p); c.y=abs(c.y-0.5)+0.25;
        if(mod(floor(p.y),2.)>0.5) c.x=1.-c.x;
        c.x += 0.1;
        c.y += 0.3;
        return motif(c);
    }
    if(gid==7){ // pgg
        vec2 c=fract(p);
        if(mod(floor(p.x),2.0)>0.5) c.y=1.0-c.y;
        if(mod(floor(p.y),2.0)>0.5) c.x=1.0-c.x;
        return motif(c);
    }
    if(gid==8){ // cmm
        vec2 c=fract(p);
        if(mod(floor(p.x)+floor(p.y),2.0)>0.5) c=1.0-c;
        c=abs(c-0.5)+0.5;
        return motif(c);
    }
    if(gid==9){ // p4
        vec2 c=fract(p); vec2 q=c-0.5;
        float an=mod(atan(q.y,q.x),PI*0.5);
        c=length(q)*vec2(cos(an),sin(an))+0.5;
        return motif(c);
    }
    if(gid==10){ // p4m
        vec2 c=fract(p); vec2 q=abs(c-0.5);
        if(q.x<q.y) q=q.yx;
        return motif(q+0.5);
    }
    if(gid==11){ // p4g
        vec2 c=fract(p);
        vec2 q=c-0.5;
        // 4-fold rotation
        float an=atan(q.y,q.x);
        an=mod(an,PI*0.5);
        q=length(q)*vec2(cos(an),sin(an));
        // glide: flip diagonal in alternating tiles
        if(mod(floor(p.x)+floor(p.y),2.0)>0.5) q.xy=q.yx;
        return motif(q+0.5);
    }
    if(gid==12) return hexFold(p, 0); // p3
    if(gid==13) return hexFold(p, 1); // p3m1
    if(gid==14) return hexFold(p, 2); // p31m
    if(gid==15) return hexFold(p, 3); // p6
    if(gid==16) return hexFold(p, 4); // p6m
    return vec3(0.0);
}

// 5x7 bitmap font - each char encoded as 5 columns of 7 bits (low bit = top row)
// Stored as an int per column. We need: p,1,2,3,4,6,m,g,c
// Char lookup: returns 5 column bitmasks (7 bits each)
// We pack all 5 columns into a single int: col0 in bits 0-6, col1 in 7-13, etc.
// But that's 35 bits, too many. Use two ints or a different approach.
// Instead: use a function that returns pixel on/off for a 5x7 grid.

float glyph(int ch, int x, int y){
    // ch: ASCII-ish. x: 0-4, y: 0-6 (0=top)
    if(x<0||x>4||y<0||y>6) return 0.0;
    // Encode each row as 5 bits (bit4=left, bit0=right)
    // Pack rows 0-5 into dLo (bits 0-29), row 6 separately in dHi (bits 0-4)
    // to avoid 32-bit int overflow
    int dLo = 0, dHi = 0;
    if(ch==112){ // 'p'
        //11110  10001  10001  11110  10000  10000  10000
        dLo=(0x1E)|(0x11<<5)|(0x11<<10)|(0x1E<<15)|(0x10<<20)|(0x10<<25);
        dHi=0x10;
    } else if(ch==109){ // 'm'
        //10001  11011  10101  10101  10001  10001  10001
        dLo=(0x11)|(0x1B<<5)|(0x15<<10)|(0x15<<15)|(0x11<<20)|(0x11<<25);
        dHi=0x11;
    } else if(ch==103){ // 'g'
        //01110  10001  10000  10111  10001  10001  01110
        dLo=(0x0E)|(0x11<<5)|(0x10<<10)|(0x17<<15)|(0x11<<20)|(0x11<<25);
        dHi=0x0E;
    } else if(ch==99){ // 'c'
        //01110  10001  10000  10000  10000  10001  01110
        dLo=(0x0E)|(0x11<<5)|(0x10<<10)|(0x10<<15)|(0x10<<20)|(0x11<<25);
        dHi=0x0E;
    } else if(ch==49){ // '1'
        //00100  01100  00100  00100  00100  00100  01110
        dLo=(0x04)|(0x0C<<5)|(0x04<<10)|(0x04<<15)|(0x04<<20)|(0x04<<25);
        dHi=0x0E;
    } else if(ch==50){ // '2'
        //01110  10001  00001  00010  00100  01000  11111
        dLo=(0x0E)|(0x11<<5)|(0x01<<10)|(0x02<<15)|(0x04<<20)|(0x08<<25);
        dHi=0x1F;
    } else if(ch==51){ // '3'
        //01110  10001  00001  00110  00001  10001  01110
        dLo=(0x0E)|(0x11<<5)|(0x01<<10)|(0x06<<15)|(0x01<<20)|(0x11<<25);
        dHi=0x0E;
    } else if(ch==52){ // '4'
        //00010  00110  01010  10010  11111  00010  00010
        dLo=(0x02)|(0x06<<5)|(0x0A<<10)|(0x12<<15)|(0x1F<<20)|(0x02<<25);
        dHi=0x02;
    } else if(ch==54){ // '6'
        //01110  10000  10000  11110  10001  10001  01110
        dLo=(0x0E)|(0x10<<5)|(0x10<<10)|(0x1E<<15)|(0x11<<20)|(0x11<<25);
        dHi=0x0E;
    }
    int row = (y < 6) ? ((dLo >> (y*5)) & 0x1F) : dHi;
    // bit4 = leftmost pixel
    return float((row >> (4-x)) & 1);
}

// Draw a string for a given group id. Returns brightness.
// lp is 0..1 within the label area.
float drawGroupLabel(vec2 lp, int gid){
    // Group names by gid:
    // 0:p1 1:p2 2:pm 3:pg 4:cm 5:pmm 6:pmg 7:pgg 8:cmm
    // 9:p4 10:p4m 11:p4g 12:p3 13:p3m1 14:p31m 15:p6 16:p6m
    // Encode as arrays of ASCII codes, max 4 chars
    // p=112 m=109 g=103 c=99 1=49 2=50 3=51 4=52 6=54 0=0(end)
    int c0=0,c1=0,c2=0,c3=0; int len=0;
    if(gid== 0){c0=112;c1= 49;len=2;}                          // p1
    if(gid== 1){c0=112;c1= 50;len=2;}                          // p2
    if(gid== 2){c0=112;c1=109;len=2;}                          // pm
    if(gid== 3){c0=112;c1=103;len=2;}                          // pg
    if(gid== 4){c0= 99;c1=109;len=2;}                          // cm
    if(gid== 5){c0=112;c1=109;c2=109;len=3;}                   // pmm
    if(gid== 6){c0=112;c1=109;c2=103;len=3;}                   // pmg
    if(gid== 7){c0=112;c1=103;c2=103;len=3;}                   // pgg
    if(gid== 8){c0= 99;c1=109;c2=109;len=3;}                   // cmm
    if(gid== 9){c0=112;c1= 52;len=2;}                          // p4
    if(gid==10){c0=112;c1= 52;c2=109;len=3;}                   // p4m
    if(gid==11){c0=112;c1= 52;c2=103;len=3;}                   // p4g
    if(gid==12){c0=112;c1= 51;len=2;}                          // p3
    if(gid==13){c0=112;c1= 51;c2=109;c3=49;len=4;}             // p3m1
    if(gid==14){c0=112;c1= 51;c2=49;c3=109;len=4;}             // p31m
    if(gid==15){c0=112;c1= 54;len=2;}                          // p6
    if(gid==16){c0=112;c1= 54;c2=109;len=3;}                   // p6m

    // Character cell size in label-UV space
    float charW = 0.12;
    float charH = 0.9;
    float totalW = charW * float(len);
    float startX = 0.5 - totalW * 0.5; // center

    float px = (lp.x - startX) / charW;
    int ci = int(floor(px));
    if(ci < 0 || ci >= len) return 0.0;

    float cx = fract(px);
    float cy = 1.0 - lp.y; // flip y so 0=top

    // Pixel coords in 5x7 grid with small margin
    float mx = 0.1, my = 0.05;
    float gx = (cx - mx) / (1.0 - 2.0*mx) * 5.0;
    float gy = (cy - my) / (1.0 - 2.0*my) * 7.0;
    int ix = int(floor(gx));
    int iy = int(floor(gy));

    int ch = 0;
    if(ci==0) ch=c0;
    if(ci==1) ch=c1;
    if(ci==2) ch=c2;
    if(ci==3) ch=c3;

    return glyph(ch, ix, iy);
}

// Convert screen UV (0..1) to grid gid, returns -1 if out of bounds
int uvToGid(vec2 px, out vec2 cellUV){
    float rowH = 1.0 / 3.0;
    int row = clamp(int(floor(px.y / rowH)), 0, 2);
    float rowY = px.y / rowH - float(row);
    int rowCols = (row == 0) ? 5 : 6;
    float margin = (row == 0) ? (1.0 - 5.0/6.0) * 0.5 : 0.0;
    float colW = 1.0 / 6.0;
    float localX = (px.x - margin) / colW;
    int col = int(floor(localX));
    float colX = fract(localX);
    int gid = (2 - row) * 6 + col;
    cellUV = vec2(colX, rowY);
    if(col < 0 || col >= rowCols || gid < 0 || gid > 16) return -1;
    return gid;
}

void mainImage(out vec4 fragColor, in vec2 fragCoord){
    vec2 px = fragCoord / iResolution.xy; // 0..1

    // ── Read persistent state from pixel (0,0) ──
    vec4 stateRaw = texelFetch(iChannel0, ivec2(0,0), 0);
    int selGid = (iFrame < 1) ? -1 : int(stateRaw.x + 0.5) - 1; // stored as gid+1, 0 means none
    float lastClickTime = stateRaw.y;

    // Layout: 3 rows, 6 cols. Bottom row (row 0) has 5 boxes centered.
    // Row 2 (top): 6 boxes, Row 1 (mid): 6 boxes, Row 0 (bot): 5 boxes
    int cols = 6;
    float rowH = 1.0 / 3.0;
    int row = int(floor(px.y / rowH));
    row = clamp(row, 0, 2);
    float rowY = px.y / rowH - float(row); // 0..1 within row

    int rowCols = (row == 0) ? 5 : 6;
    float margin = (row == 0) ? (1.0 - 5.0/6.0) * 0.5 : 0.0; // center 5 boxes
    float colW = 1.0 / 6.0;
    float localX = (px.x - margin) / colW;
    int col = int(floor(localX));
    float colX = fract(localX); // 0..1 within cell

    // Map row,col to group index (row 2=top is groups 0-5, row 1=6-11, row 0=12-16)
    int gid = (2 - row) * 6 + col;

    // ── Handle state storage in pixel (0,0) ──
    if(fragCoord.x < 1.0 && fragCoord.y < 1.0){
        float newSel = float(selGid + 1); // +1 offset so 0 = none
        float newClickTime = lastClickTime;

        // Detect new click (mouse button pressed: iMouse.z > 0 means currently pressed)
        if(iMouse.z > 0.0 && (iTime - lastClickTime) > 0.3){
            vec2 clickPx = iMouse.xy / iResolution.xy;
            if(selGid >= 0){
                // Currently fullscreen — check if clicking the X button (top-right corner)
                // or just click anywhere to exit
                newSel = 0.0; // back to grid (0 = none)
                newClickTime = iTime;
            } else {
                // Grid mode — find which box was clicked
                vec2 dummyUV;
                int clickedGid = uvToGid(clickPx, dummyUV);
                if(clickedGid >= 0){
                    newSel = float(clickedGid + 1);
                    newClickTime = iTime;
                }
            }
        }

        fragColor = vec4(newSel, newClickTime, 0.0, 1.0);
        return;
    }

    // ── Fullscreen mode ──
    if(selGid >= 0){
        // Fill screen with the selected pattern
        float aspect = iResolution.x / iResolution.y;
        // Make pattern square, fit to view (shorter axis = 1 tile of pattern)
        vec2 patUV = px - 0.5;
        if(aspect > 1.0){
            patUV.x *= aspect;
        } else {
            patUV.y /= aspect;
        }
        // Scale to show ~3 tiles like the grid cells
        patUV = patUV * 3.0 + 0.5;
        vec3 col3 = applyGroup(patUV, selGid);

        // Draw label bar at bottom
        float labelH = 0.06;
        if(px.y < labelH){
            col3 = vec3(0.05);
            vec2 lp = vec2(px.x, px.y / labelH);
            float hue = float(selGid) / 17.0;
            vec3 labelCol = 0.4 + 0.4 * cos(6.2831 * (hue + vec3(0.0, 0.33, 0.67)+time/4.));
            float txt = drawGroupLabel(lp*1.5-0.25, selGid);
            col3 = mix(col3, labelCol, txt * 0.9);
        }

        // Draw small "X" button indicator in top-right
        vec2 xp = (px - vec2(0.96, 0.96)) * vec2(aspect, 1.0);
        float xd = max(abs(xp.x), abs(xp.y));
        float xBtn = smoothstep(0.02, 0.015, xd);
        float cross = smoothstep(0.003, 0.001, min(abs(xp.x - xp.y), abs(xp.x + xp.y)));
        col3 = mix(col3, vec3(0.15), xBtn * 0.8);
        col3 = mix(col3, vec3(1.0, 0.3, 0.3), xBtn * cross);

        fragColor = vec4(pow(col3, vec3(0.85)), 1.0);
        return;
    }

    // Background for out-of-bounds cells
    vec3 bg = vec3(0.08);
    if(col < 0 || col >= rowCols || gid < 0 || gid > 16){
        fragColor = vec4(bg, 1.0);
        return;
    }

    // Cell UV with padding for border
    float pad = 0.015;
    vec2 cellUV = vec2(colX, rowY);

    // Border
    float bx = min(cellUV.x, 1.0 - cellUV.x);
    float by = min(cellUV.y, 1.0 - cellUV.y);
    float border = min(bx, by);
    float borderMask = smoothstep(0.0, pad, border);

    // Label strip at bottom of cell (bottom 15%)
    float labelH = 0.15;
    vec3 col3;
    if(cellUV.y < labelH){
        col3 = vec3(0.05);
        vec2 lp = vec2(cellUV.x, cellUV.y / labelH);
        float hue = float(gid) / 17.0;
        vec3 labelCol = 0.4 + 0.4 * cos(6.2831 * (hue + vec3(0.0, 0.33, 0.67)+time/4.));
        float txt = drawGroupLabel(lp*1.5-0.25, gid);
        col3 = mix(col3, labelCol, txt * 0.9);
    } else {
        // Pattern area: remap cellUV.y from labelH..1 to 0..1
        float patH = 1.0 - labelH;
        vec2 patUV = vec2(cellUV.x, (cellUV.y - labelH) / patH);

        // Make pattern area square (fit to view within the cell)
        // Cell aspect ratio in screen space: cellW / cellH_pattern
        float cellW = iResolution.x / 6.0;
        float cellH = iResolution.y / 3.0 * patH;
        float cellAspect = cellW / cellH;

        // Center and correct so pattern is square
        patUV -= 0.5;
        if(cellAspect > 1.0)
            patUV.x *= cellAspect;
        else
            patUV.y /= cellAspect;
        patUV += 0.5;

        col3 = applyGroup(patUV, gid);
    }

    // Apply border
    col3 = mix(vec3(0.15), col3, borderMask);

    fragColor = vec4(pow(col3, vec3(0.85)), 1.0);
}

Image

Not used

More shaders by scry

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

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

Account

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