block by Kcnarf 7855451634a2d91ccbf24cc31b9a6be0

Blobbing Particles

Full Screen

This block is a recreation:

I guess the strange flickering effect comes from the definition of a radial gradient: it’s center and width of the radial gradient are defined as a percentage. These percentages are computed among the BBox of the element on which the radial gradient is applied on. Because a cell’s widht/height is constantly evolving (depending on neighbours proximity), the radial gradient’s center/widht is also constantly evolving/shrinking.

See this block for a smoother version.

Acknowledgments to:

index.html

<meta charset="utf-8">
<style>
  
  .layer {
    opacity: 0.5;
  }

  .particle .seed {
    fill: black;
  }
  
  .particle .influence-zone {
    fill: url("#radial-gradient");
    stroke: none;
  }

</style>
<body>
  <svg>
    <defs>
      <radialGradient id="radial-gradient" cx="50%" cy="50%" r="50%">
        <stop offset="25%" stop-color="white"></stop>
        <stop offset="100%" stop-color="black"></stop>
      </radialGradient>
    </defs>
  </svg>
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="https://raw.githack.com/Kcnarf/d3-distanceLimitedVoronoi/master/distance-limited-voronoi.js"></script>
  <script>
    var width = 960,
        height = 500,
        influenceRadius = 20;
		
    var layerCount = 2,
        particleCountPerLayer = 25,
        particles = new Array(layerCount);
    
    for (var l = 0; l < layerCount; ++l) {
      particles[l] = new Array(particleCountPerLayer);
      for (var i = 0; i < particleCountPerLayer; ++i) {
        particles[l][i] = {
          x: Math.random() * width,
          y: Math.random() * height,
          vx: 0,
          vy: 0
        };
      }
    }
		
    var limitedVoronoi = d3.distanceLimitedVoronoi()
    			.limit(influenceRadius)
    			.extent([[-influenceRadius,-influenceRadius],[width+influenceRadius,height+influenceRadius]])
    			.x( function(d) { return d.x })
    			.y( function(d) { return d.y });
		
    var svg = d3.select("svg");
   	svg.attr("width", width)
    		.attr("height", height);
    
    svg.selectAll("g").data(d3.range(layerCount))
      	.enter()
      		.append("g")
    				.classed("layer", true)
    				.attr("id", function(d) { return "layer-"+d; });

    for (var l = 0; l < layerCount; l++) {
      var layer = svg.select("#layer-"+l);
      var clipPathName = "clip-"+l+"-";
      var drawnParticles = layer.selectAll(".particle").data(particles[0]);
      enteringParticles = drawnParticles.enter()
        .append("g")
          .classed("particle", true);
      enteringParticles
        .append("path")
          .classed("influence-zone", true)
    }
    
    d3.timer(function(elapsed) {
      for (var l = 0; l < layerCount; l++) {
        var layer = svg.select("#layer-"+l);
        for (var i = 0; i < particleCountPerLayer; ++i) {
          var p = particles[l][i];
          p.x += p.vx;
          p.y += p.vy;
          if (p.x < -influenceRadius) {
            p.x += (width+2*influenceRadius);
          } else if (p.x > width+influenceRadius) {
            p.x -= (width+2*influenceRadius);
          }
          if (p.y < -influenceRadius) {
            p.y += (height+2*influenceRadius);
          } else if (p.y > height+influenceRadius) {
            p.y -= (height+2*influenceRadius);
          }
          p.vx += 0.2 * (Math.random() - .5) - 0.01 * p.vx;
          p.vy += 0.2 * (Math.random() - .5) - 0.01 * p.vy;
        }

        var limitedCells = limitedVoronoi(particles[l]);

        layer.selectAll(".particle .influence-zone").data(limitedCells)
          .attr("d", function(d) { return d.path; });
      }
    });

  </script>
</body>