p5js Worms

One thing that I’ve been hoping to get into a bit more is the idea of Generative Art. Essentially, use any of a wide variety of algorithms to generate art. To do that, and so that the art can be generated right in front of you in the browser, I’m going to use the p5js library. It gives you a nice API of graphical primitives and takes a simple setup and draw function and does the rest.

For example:


const WORM_COUNT = 10;
const UPDATES_PER_FRAME = 100;

var worms = [];

function Worm(x, y, c) {
  this.x = x || random(width);
  this.y = y || random(height);
  this.c = c || color(
    random(256),
    random(256),
    random(256)
  );
}

Worm.prototype.update = function() {
  this.x = (this.x + random(-1, 1) + width) % width;
  this.y = (this.y + random(-1, 1) + height) % height;
}

Worm.prototype.draw = function() {
  fill(this.c);
  noStroke();
  rect(this.x, this.y, 1, 1);
}

function setup() {
  createCanvas(400, 400);

  for (var i = 0; i < WORM_COUNT; i++) {
    worms.push(new Worm());
  }
}

function draw() {
  for (var i = 0; i < UPDATES_PER_FRAME; i++) {
    for (worm of worms) {
      worm.update();
      worm.draw();
    }
  }
}

function keyTyped() {
  if (key === 's') {
    saveCanvas('photo', 'png');
  }
}

This specific example is a rewrite of one of teh very oldest bits of code I’d ever written: worms. Simple little dots that wonder randomly around the screen. I wrote my very firt copy of that probably 20 or more years ago in QBasic. Yes, I started with Basic. And I turned out (arguably) okay. :D

Codewise, it’s not super interesting, but it does serve as something of a proof of concept for embedding p5.js in Hugo (my current static site generator). To do that, I’ve written up a quick shortcode:

{{ $width := mul 1.1 (int (or ($.Get "width") 400)) }}
{{ $height := mul 1.1 (int (or ($.Get "height") 400)) }}
{{ $header := `
<html>
<head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js" integrity="sha512-WIklPM6qPCIp6d3fSSr90j+1unQHUOoWDS4sdTiR8gxUTnyZ8S2Mr8e10sKKJ/bhJgpAa/qG068RDkg6fIlNFA==" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/addons/p5.sound.min.js" integrity="sha512-wM+t5MzLiNHl2fwT5rWSXr2JMeymTtixiw2lWyVk1JK/jDM4RBSFoH4J8LjucwlDdY6Mu84Kj0gPXp7rLGaDyA==" crossorigin="anonymous"></script>    
    <style>canvas { border: 1px solid black; }</style>
</head>
<body>
` }}
{{ $footer := `
<noscript>To display this p5.js sketch, JavaScript must be enabled.</noscript>
</body>
</html>
`}}

<iframe 
    width="{{ $width }}" height="{{ $height }}" frameBorder="0"
    srcdoc="{{ $header }}<script>{{ .Inner }}</script>{{ $footer }}"
></iframe>

{{ (printf "```javascript\n%s\n```" .Inner) | markdownify }}

I’m using an iframe srcdoc specifically so I can embed more than one of these on the same page. Otherwise, p5.js gets a little finicky about it. It’s actually pretty cool how it works.

In the future, I’ll probably add Run/Pause buttons, a button to save the current image and probably a few other quality of life features, but for now / a minimum viable product, I think it’s pretty shiny!