<canvas id="canvas"></canvas>
html, body {
  background: #000022;
  margin: 0;
  overflow: hidden;
  text-align: center;
}
/*
  A visualisation of the
  Diffusion-limited aggregation
  algorithm.
  By Johan Karlsson
  
  Thanks to Paul Bourke!
  http://paulbourke.net/fractals/dla/
*/

var nrOfSpawnedParticles = 0;

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

// Make the canvas square
var min = Math.min(window.innerWidth, window.innerHeight);
canvas.width = min;
canvas.height = min;

// The bigger canvas, the bigger particles.
var particleSize = Math.round(min/120);
var nrOfParticles = Math.round(min/particleSize/2);
// Virtual width and height used as matrix size below
var width = Math.round(canvas.width / particleSize);
var height = Math.round(canvas.height / particleSize);
var midY = Math.round(height/2);
var midX = Math.round(width/2);

// Build a matrix that corresponds to all
// virtual pixels (particleSize) on the canvas.
var matrix = new Array(width);
for(var x = 0; x < width; x++) {
  matrix[x] = new Array(height);
  matrix[x][0] = 1;
  for(var y = 1; y < height; y++) { 
    matrix[x][y] = 0;
    if(x === 0 || x === width-1) {
      matrix[x][y] = 1;
    }
  }    
  matrix[x][height-1] = 1;
  ctx.fillRect(x, height-1, 1, 1);
}

var Particle = function(color) {
  this.color = color;
  this.x = width/2;
  this.y = height/2;
  this.speed = 1;
  this.angle = Math.random() * 2 * Math.PI;
}

Particle.prototype.move = function () {
  this.angle += Math.random() - 0.5;

  this.x += Math.cos(this.angle) * this.speed;
  this.y += Math.sin(this.angle) * this.speed;

  // Wrap around the screen
  if(this.x >= width) {
    this.x = 0;
  } else if(this.x < 0) {
    this.x = width - 1;
  }
  if(this.y >= height) {
    this.y = 0;
  } else if(this.y < 0) {
    this.y = height - 1;
  }
}

Particle.prototype.draw = function () {
  ctx.fillStyle = 
    "hsl(" + this.color + ", 100%, 50%)";

  ctx.fillRect(
    this.x*particleSize, 
    this.y*particleSize, 
    particleSize, 
    particleSize);    
}


function isCollition(x, y) {
  if(x >= matrix.length || 
     x < 0 || 
     y >= matrix[x].length || 
     y < 0) {
    return false;
  }
  return matrix[x][y] !== 0;
}

var p;
var particles = [];
for(var i = 0; i < nrOfParticles; i++) {
  particles.push(new Particle(0));
}

var x, y;
// Brownian walk
function walk() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  for(var xm = 0; xm < matrix.length; xm++){
    for(var ym = 0; ym < matrix[xm].length; ym++) {
      if(matrix[xm][ym] !== 0) {
        ctx.fillStyle = 
          "hsl(" + matrix[xm][ym] + ", 100%, 50%)";
        ctx.fillRect(
          xm*particleSize, 
          ym*particleSize, 
          particleSize, 
          particleSize);
      }
    }
  }
  // Walk around till we hit something
  for(var i = 0; i < particles.length; i++) {
    p = particles[i];
    p.move();
    p.draw();
    x = Math.round(p.x);
    y = Math.round(p.y);
    if(isCollition(x-1, y-1) ||
       isCollition(x  , y-1) ||
       isCollition(x+1, y-1) ||
       isCollition(x-1, y  ) ||
       isCollition(x  , y  ) ||
       isCollition(x+1, y  ) ||
       isCollition(x-1, y+1) ||
       isCollition(x  , y+1) ||
       isCollition(x+1, y+1)) {
      if(x >= 0 && x < width && y >= 0 && y < height) {
        if(y > midY - 2 && y < midY + 2 &&
           x > midX - 2 && x < midX + 2) {
          console.log("Done!");
          return;
        }

        // We have a collition
        // Put a mark in the matrix
        matrix[x][y] = p.color;
        // ...and on the canvas
        ctx.fillRect(
          p.x*particleSize, 
          p.y*particleSize, 
          particleSize, 
          particleSize);

        // Spawn a new particle
        particles[i] = new Particle(nrOfSpawnedParticles/height/width*2000);

        nrOfSpawnedParticles++;
      }
    }
  }
  requestAnimationFrame(walk);
}

walk();
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.