About this technique →
sdf-composition/sketch.js
export const PARAMS = {
  count:  { value: 5, min: 2, max: 10, step: 1, label: "Shape Count", folder: "Structure" },
  radius: { value: 120, min: 30, max: 300, step: 1, label: "Radius", folder: "Structure" },
  smooth: { value: 60, min: 1, max: 200, step: 1, label: "Blend Smoothness", folder: "Structure" },
  bands:  { value: 8, min: 2, max: 30, step: 1, label: "Iso Bands", folder: "Appearance" },
  hue:    { value: 220, min: 0, max: 360, step: 1, label: "Base Hue", folder: "Appearance" },
  cspread:{ value: 90, min: 0, max: 180, step: 1, label: "Hue Spread", folder: "Appearance" },
  step:   { value: 4, min: 1, max: 12, step: 1, label: "Sample Step (px)", folder: "Performance" },
};

export const SHARE = { bookmarked: ["count", "radius", "smooth", "bands", "hue", "cspread"] };

export function sketchSetup(ctx, w, h, tng) {
  const centers = [];
  for (let i = 0; i < 10; i++) {
    centers.push([tng.randomRange(w * 0.2, w * 0.8), tng.randomRange(h * 0.2, h * 0.8)]);
  }
  return { centers };
}

export function sketchDraw(ctx, w, h, params, tng, state) {
  const img = ctx.createImageData(w, h);
  const data = img.data;
  const step = params.step;

  for (let y = 0; y < h; y += step) {
    for (let x = 0; x < w; x += step) {
      let d = Infinity;
      for (let i = 0; i < params.count; i++) {
        const c = state.centers[i];
        const di = tng.sdf.circle(x, y, c[0], c[1], params.radius);
        d = i === 0 ? di : tng.sdf.smin(d, di, params.smooth);
      }
      const band = Math.floor((d / params.radius + 1) * params.bands * 0.5);
      const t = Math.abs(((band % 2) + 2) % 2);
      const hue = (params.hue + (band / params.bands) * params.cspread) % 360;
      const [r, g, b] = tng.color.oklchToRgb(0.4 + t * 0.4, 0.12, hue);
      const sx = step, sy = step;
      for (let dy = 0; dy < sy && y + dy < h; dy++) {
        for (let dx = 0; dx < sx && x + dx < w; dx++) {
          const idx = ((y + dy) * w + (x + dx)) * 4;
          data[idx] = r * 255;
          data[idx + 1] = g * 255;
          data[idx + 2] = b * 255;
          data[idx + 3] = 255;
        }
      }
    }
  }
  ctx.putImageData(img, 0, 0);
}