Today, I’m going to work on using worms to generate coral, similar to what I did way back when I was generating omnichromatic images.

In a nutshell:
- Spawn n worms
- On each tick:- Each worm tries to randomly move one direction
- If it cannot, increment that worm’s stuckcounter
- If it can, restart the stuckcounter
- If a worm is stucklong enough, kill it off and spawn a new worm
 
Eventually, we’ll fill the entire space with colors that end up looking a bit like coral. I’ll probably extend this later, since there are a lot of cool tweaks you can do with this general idea.
let gui;
let params = {
  pointCount: 10,
  maxAge: 0,
  maxStuck: 10,
};
let visited;
let points;
function setup() {
  createCanvas(400, 400);
  gui = createGuiPanel();
  gui.addObject(params);
  gui.setPosition(420, 0);
  reset();
}
function reset() {
  clear();
  background(0);
  
  visited = [];
  for (var x = 0; x < width; x++) {
    visited[x] = [];
    for (var y = 0; y < height; y++) {
      visited[x][y] = false;
    }
  }
  
  points = [];
}
function draw() {
  while (points.length < params.pointCount) {
    points.push({
      x: int(random(width)),
      y: int(random(height)),
      c: color(random(256), random(256), random(256)),
      age: 0,
      stuck: 0,
    })
  }
  for (var i = 0; i < points.length; i++) {
    var point = points[i];
    point.age++;
    
    fill(point.c);
    noStroke();
    rect(point.x, point.y, 1, 1);
    
    // Try to move each point
    var newX = point.x + random([-1, 0, 1]);
    var newY = point.y + random([-1, 0, 1]);
    
    if (newX == point.x && newY == point.y) {
      continue;
    } else if (newX < 0 || newX >= width || newY < 0 || newY >= height || visited[newX][newY]) {
      point.stuck++;
    } else {
      point.x = newX;
      point.y = newY;
      visited[newX][newY] = true;
    }
    
    // Remove old points
    if (params.maxAge > 0 && point.age > params.maxAge) {
      points.splice(i, 1);
      i--;
    }
    // Remove 'stuck' points
    if (params.maxStuck > 0 && point.stuck > params.maxStuck) {
      points.splice(i, 1);
      i--;
    }
  }
}
Pretty cool!
I’ve also updated my p5js Hugo shortcode with a bit of a neat trick to add the save and clear buttons:
{{ $additionalScript := `
var oldSetup = setup;
setup = () => {
    oldSetup();
    
    createButton("play/pause").mousePressed(() => {
      if (isLooping()) {
        noLoop();
      } else {
        loop();
      }
    });
    createButton("save").mousePressed(() => {
        saveCanvas('photo', 'png')
    });
    createButton("clear").mousePressed(() => {
        if (typeof reset !== 'undefined') {
            reset();
        } else {
            clear();
        }
    });
}
`}}
<iframe 
    width="{{ $width }}" height="{{ $height }}" frameBorder="0"
    srcdoc="{{ $header }}<script>{{ .Inner }}{{ $additionalScript }}</script>{{ $footer }}"
></iframe>
So after the script that I’m embedding I will be overwriting the script provided setup function with one that can add the buttons:
- play/pausewill stop the p5js loop or start it again
- savewill call the built in- saveCanvasto generate a PNG and export it
- clearwill call the custom- resetfunction if defined (to do additional work, like resetting the worms in this case), the built in- clearif not
And because I’m modifying the shortcode, this will apply to all previous sketches as well. Woot.