Procedural Animation with Canvas, random walks
Tags: javascript, canvas
Published: 2021-07-23 17:18:25

HTML5 comes with Canvas, a beautiful blank canvas for us to paint on.
It can do a lot. I'll be going through how to create a simple procedural animation! It shouldn't take long to get a random walker moving around the screen. After we get our pixel friend moving, we'll play with it's color. Then we'll get a few walkers moving on the canvas at the same time. Finally, we'll connect the dots to get our walkers painting an abstract. 

Our final walkers will draw something like this:
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z6ow7mtfx3umfq4kjccr.png)
 
But first, we need a canvas!

```
<canvas id='mainCanvas'>Displayed if canvas isn't supported.</canvas>
```

I like the canvas to fill our whole view:

```javascript
canvas.width = window.innerWidth
canvas.height = window.innerHeight
```

Once we have our canvas, we need it's [context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D), the object we use to manipulate our canvas.
```javascript
ctx = canvas.getRenderContext('2d')
```


That's all we need to start rendering shapes to our canvas.

```javascript
let rectWidth = 5
let rectHeight = 5
ctx.fillStyle = 'black'
ctx.rect( canvas.width/2, canvas.height/2, rectWidth, rectHeight)
ctx.fill()
```

![point in the center](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pyfbjg60lw1oi2tl82s4.png)
[open on codepen](https://codepen.io/mpixdb/pen/bGWrYYg)
Now we have a point in the center of the page!

To animate the point, we need two things.

An animation loop and some rendering logic for the point that will be called each time the loop runs.


Our rendering logic will be ultra simple for now: at each step, we will move the x and y portions of our point by a random amount.
Then we will draw it to the screen.

```javascript
function render(){
  point.x += (Math.random()-.5)*5
  point.y += (Math.random() -.5)*5
  ctx.rect( point.x, point.y, 5, 5)
  ctx.fill()
  window.requestAnimationFrame(render);
}
```

Then we get our animation loop from [window.requestAnimationFrame(callback)](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame), which fires when the window is ready to draw a new frame.
We then give it our callback function that renders the new frame. (If animation speed is of concern, we should use the time passed to the callback to keep the animation steady across monitors with different refresh rates)

{% codepen https://codepen.io/mpixdb/pen/dyWzZgb default-tab=result %}
We have our animation running, just like that. Feel free to play with it on codepen!

Now we are going to play a little bit with color.

{% codepen https://codepen.io/mpixdb/pen/eYWGJaO default-tab=result %}

```javascript
ctx.fillStyle = `rgba(${time%255}, ${time%50}, ${time%127}, 1)`
```
The context fillStyle can understand any valid CSS color value, including named strings, rgba() values, as well as many [others](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle).
We use the passed time along with the modulo operator to create a cycling color pallet. The red channel varies from 0-255, the green from 0 to 50, and the blue from 0 to 127. 

Next, we'll add a few random walkers to our screen. Why? Because we can! 

First we setup our walkers. 
```javascript
let points = []
for(let i = 0; i < numPoints; i++){
  let point = {x: canvas.width/numPoints*i, 
             y: canvas.height/2}
  points.push(point)
}
```

Then we update each of the walkers in our render function.

```javascript
points.forEach( (point, i) =>{ 
    ctx.beginPath()
    point.x += (Math.random()-.5)*5
    point.y += (Math.random() -.5)*5
    ctx.rect( point.x, point.y, 5, 5)
    ctx.fill()
  })
```
{% codepen https://codepen.io/mpixdb/pen/vYmJRoG default-tab=result %}

What happens when we connect all the walkers with a colored line?
Let's find out!

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fswsw2znmy4ebya9pqgh.png)

with 'hue' [compositing](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation)
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zc52dt3h6tpl29ipcgxm.png)

Using the center point as the connected point, dodge compositing, smaller walkers, and a bigger y-direction jump.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dk5q14qv7fhiarafnnve.png)
Same setup, longer run
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/umd049fvp50jifykxl5s.png)

Here is the codepen that generated it

{% codepen https://codepen.io/mpixdb/pen/XWRewyN default-tab=result %}