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
- Project_2026-08-15_18-22-06
- Project_2026-08-14_16-45-00
- Project_2026-07-24_15-10-23
- Project_2026-07-15_03-18-26
- Glass of Orange Juice (raymarched)
- Project_2026-07-13_00-53-50
- Project_2026-07-10_16-39-54
- Popcorn ceiling
- Project_2026-06-20_15-32-19
- Project_2026-06-25_12-22-43
- Visual Enhancer
- Project_2026-06-23_19-17-29
Browse all public shaders · All shaders by scry · ShaderKit home
Unread comments
×Projects with new comments. Click to open and read.