About this technique →
menger-sponge/sketch.js
// Menger Sponge
//
// Iterated cube-based fractal via SDF domain folding. Raymarched in GLSL.
// Camera orbits the sponge. The "menger fold" iteratively removes the
// cross-shaped tunnels at each scale.

export const PARAMS = {
  iterations: { value: 4,    min: 1, max: 6,   step: 1,    label: "Fractal Iterations", folder: "Structure" },
  twist:      { value: 0.0,  min: 0, max: 2,   step: 0.01, label: "Domain Twist",       folder: "Structure" },
  glow:       { value: 0.5,  min: 0, max: 2,   step: 0.01, label: "Glow",               folder: "Appearance" },
  hue:        { value: 200,  min: 0, max: 360, step: 1,    label: "Hue",                folder: "Appearance" },
  speed:      { value: 0.15, min: 0, max: 1.5, step: 0.01, label: "Orbit Speed",        folder: "Behavior" },
};

export function shaderUniforms(params) {
  return {
    u_iterations: { value: params.iterations | 0 },
    u_twist:      { value: params.twist },
    u_glow:       { value: params.glow },
    u_hue:        { value: params.hue },
    u_speed:      { value: params.speed },
  };
}

export function shaderAnimate(uniforms, params) {
  uniforms.u_iterations.value = params.iterations | 0;
  uniforms.u_twist.value      = params.twist;
  uniforms.u_glow.value       = params.glow;
  uniforms.u_hue.value        = params.hue;
  uniforms.u_speed.value      = params.speed;
}

export function fragmentShader() {
  return `
    uniform float u_time;
    uniform vec2  u_resolution;
    uniform int   u_iterations;
    uniform float u_twist;
    uniform float u_glow;
    uniform float u_hue;
    uniform float u_speed;

    // ── Menger SDF ──────────────────────────────────────────────────────────
    float sdBox(vec3 p, vec3 b) {
      vec3 q = abs(p) - b;
      return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
    }

    float sdMenger(vec3 p) {
      // Apply domain twist
      float twistAmt = u_twist;
      if (twistAmt > 0.0) {
        float angle = p.y * twistAmt;
        float cosA = cos(angle), sinA = sin(angle);
        p.xz = mat2(cosA, -sinA, sinA, cosA) * p.xz;
      }

      float d = sdBox(p, vec3(1.0));
      float s = 1.0;
      for (int m = 0; m < 6; m++) {
        if (m >= u_iterations) break;
        vec3 a = mod(p * s, 2.0) - 1.0;
        s *= 3.0;
        vec3 r = abs(1.0 - 3.0 * abs(a));
        float da = max(r.x, r.y);
        float db = max(r.y, r.z);
        float dc = max(r.z, r.x);
        float c = (min(da, min(db, dc)) - 1.0) / s;
        d = max(d, c);
      }
      return d;
    }

    // ── Normal ──────────────────────────────────────────────────────────────
    vec3 calcNormal(vec3 p) {
      const float h = 0.0002;
      const vec2 k = vec2(1.0, -1.0);
      return normalize(
        k.xyy * sdMenger(p + k.xyy * h) +
        k.yyx * sdMenger(p + k.yyx * h) +
        k.yxy * sdMenger(p + k.yxy * h) +
        k.xxx * sdMenger(p + k.xxx * h)
      );
    }

    // ── Palette ─────────────────────────────────────────────────────────────
    vec3 palette(float t, float baseHue) {
      float h = baseHue / 360.0 + t * 0.35;
      vec3 a = vec3(0.5);
      vec3 b = vec3(0.5);
      vec3 c = vec3(1.0);
      vec3 d = vec3(0.00, 0.10, 0.20) + h;
      return a + b * cos(6.2831853 * (c * t + d));
    }

    void main() {
      vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution) / u_resolution.y;

      // Orbiting camera
      float t = u_time * u_speed;
      vec3 ro = vec3(3.0 * cos(t), 1.2 + 0.8 * sin(t * 0.5), 3.0 * sin(t));
      vec3 ta = vec3(0.0);
      vec3 ww = normalize(ta - ro);
      vec3 uu = normalize(cross(vec3(0.0, 1.0, 0.0), ww));
      vec3 vv = cross(ww, uu);
      vec3 rd = normalize(uv.x * uu + uv.y * vv + 1.5 * ww);

      // March
      float dist = 0.0;
      float steps = 0.0;
      bool hit = false;
      for (int i = 0; i < 120; i++) {
        vec3 p = ro + rd * dist;
        float d = sdMenger(p);
        if (d < 0.0005) { hit = true; break; }
        if (dist > 12.0) break;
        dist += d * 0.9;
        steps += 1.0;
      }

      vec3 col = vec3(0.02, 0.02, 0.04);
      if (hit) {
        vec3 p = ro + rd * dist;
        vec3 n = calcNormal(p);
        vec3 ld = normalize(vec3(2.0, 4.0, 1.5));
        float diff = max(0.0, dot(n, ld));
        float spec = pow(max(0.0, dot(reflect(-ld, n), -rd)), 32.0);
        col = palette(dist * 0.15 + steps / 120.0, u_hue);
        col *= 0.4 + 0.6 * diff;
        col += 0.3 * spec;
      }
      col += u_glow * vec3(steps / 120.0) * palette(steps / 120.0, u_hue) * 0.4;
      col = pow(max(col, vec3(0.0)), vec3(1.0 / 2.2));
      gl_FragColor = vec4(col, 1.0);
    }
  `;
}