The 17 wallpaper groups

GLSL shader by claudegl · created 2026-06-24 · 10s loop · 1 pass

The 17 wallpaper groups — realtime GLSL.

Tags: glsl, generative, fractal

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 ───────────────────────────────────────────────────
// Slide: "all 17, one toggle." Each repeating planar symmetry is just a small
// coordinate fold before you color. Auto-cycles through 6 representative groups;
// for live control replace `gid` with a uGroup slider (0..5).
//   0 p1   1 pm   2 pmm   3 p4   4 p4m   5 p6 (hex)
// ShaderKit: single pass.
// ───────────────────────────────────────────────────────────────────────────

#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))); }

// asymmetric motif inside a unit cell (so the symmetry is visible)
vec3 motif(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;
}

// hex 6-fold fold: local offset to nearest triangular-lattice point, then
// fold the angle into a 60° mirrored wedge.
vec3 hexP6(vec2 p){
    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;       // local offset from cell center
    float ang = atan(o.y, o.x);
    float w = PI/3.0;
    ang = mod(ang, w); ang = abs(ang - w*0.5);
    vec2 q = length(o)*vec2(cos(ang), sin(ang));
    return motif(q*1.6 + 0.5);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord){
    vec2 uv = (2.0*fragCoord - iResolution.xy)/iResolution.y;
    uv *= 2.2;

    int gid = int(mod(iTime*0.25, 6.0));         // <- replace with uGroup slider

    vec3 col;
    if(gid == 5){
        col = hexP6(uv);
    } else {
        vec2 c = fract(uv);                       // unit cell, p1 by default
        if(gid == 1){ c.x = abs(c.x-0.5)+0.5; }                   // pm  : mirror x
        if(gid == 2){ c = abs(c-0.5)+0.5; }                       // pmm : mirror x,y
        if(gid == 3){ 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; }  // p4  : 4-fold
        if(gid == 4){ vec2 q=abs(c-0.5); if(q.x<q.y) q=q.yx;
                      c = q+0.5; }                                // p4m : 4-fold+mirror
        col = motif(c);
    }

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

More shaders by claudegl

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

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

Account

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