About this technique →
voronoi-3d-shader/sketch.js
export const PARAMS = {
  density:    { value: 6.0, min: 1.5, max: 20, step: 0.1, label: "Cell Density", folder: "Structure" },
  metric:     { value: "F1", options: ["F1", "F2-F1", "F2*F1"], label: "Distance Metric", folder: "Structure" },
  speed:      { value: 0.2, min: 0, max: 1.5, step: 0.01, label: "Animation Speed", folder: "Behavior" },
  glow:       { value: 0.3, min: 0, max: 1.5, step: 0.01, label: "Glow", folder: "Appearance" },
  hue:        { value: 280, min: 0, max: 360, step: 1, label: "Base Hue", folder: "Appearance" },
};

export function shaderUniforms(params) {
  const metricMap = { 'F1': 0, 'F2-F1': 1, 'F2*F1': 2 };
  return {
    u_density: { value: params.density },
    u_speed:   { value: params.speed },
    u_glow:    { value: params.glow },
    u_hue:     { value: params.hue },
    u_metric:  { value: metricMap[params.metric] ?? 0 },
  };
}

export function shaderAnimate(uniforms, params) {
  uniforms.u_density.value = params.density;
  uniforms.u_speed.value = params.speed;
  uniforms.u_glow.value = params.glow;
  uniforms.u_hue.value = params.hue;
  const metricMap = { 'F1': 0, 'F2-F1': 1, 'F2*F1': 2 };
  uniforms.u_metric.value = metricMap[params.metric] ?? 0;
}

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

    vec3 hash33(vec3 p) {
      p = vec3(
        dot(p, vec3(127.1, 311.7, 74.7)),
        dot(p, vec3(269.5, 183.3, 246.1)),
        dot(p, vec3(113.5, 271.9, 124.6))
      );
      return -1.0 + 2.0 * fract(sin(p) * 43758.5453123);
    }

    vec2 voronoi3(vec3 p) {
      vec3 i = floor(p);
      vec3 f = fract(p);
      float F1 = 999.0, F2 = 999.0;
      for (int z=-1; z<=1; z++) for (int y=-1; y<=1; y++) for (int x=-1; x<=1; x++) {
        vec3 g = vec3(float(x), float(y), float(z));
        vec3 o = 0.5 + 0.5 * sin(u_time * u_speed + 6.2831 * hash33(i + g));
        float d = length(g + o - f);
        if (d < F1) { F2 = F1; F1 = d; } else if (d < F2) { F2 = d; }
      }
      return vec2(F1, F2);
    }

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

    void main() {
      vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution) / u_resolution.y;
      vec3 p = vec3(uv * u_density, u_time * u_speed * 0.3);
      vec2 v = voronoi3(p);
      float f = u_metric == 0 ? v.x : (u_metric == 1 ? (v.y - v.x) : v.x * v.y);
      vec3 col = palette(f * 0.7, u_hue);
      col += u_glow * smoothstep(0.0, 0.05, v.y - v.x);
      col = pow(col, vec3(1.0 / 2.2));
      gl_FragColor = vec4(col, 1.0);
    }
  `;
}