DEV Community

loading...
Cover image for Procedural Animation with Canvas, random walks

Procedural Animation with Canvas, random walks

benjaminolmsted
・3 min read

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

But first, we need a canvas!

<canvas id='mainCanvas'>Displayed if canvas isn't supported.</canvas>
Enter fullscreen mode Exit fullscreen mode

I like the canvas to fill our whole view:

canvas.width = window.innerWidth
canvas.height = window.innerHeight
Enter fullscreen mode Exit fullscreen mode

Once we have our canvas, we need it's context, the object we use to manipulate our canvas.

ctx = canvas.getRenderContext('2d')
Enter fullscreen mode Exit fullscreen mode

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

let rectWidth = 5
let rectHeight = 5
ctx.fillStyle = 'black'
ctx.rect( canvas.width/2, canvas.height/2, rectWidth, rectHeight)
ctx.fill()
Enter fullscreen mode Exit fullscreen mode

point in the center
open on codepen
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.

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);
}
Enter fullscreen mode Exit fullscreen mode

Then we get our animation loop from window.requestAnimationFrame(callback), 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)


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.


ctx.fillStyle = `rgba(${time%255}, ${time%50}, ${time%127}, 1)`
Enter fullscreen mode Exit fullscreen mode

The context fillStyle can understand any valid CSS color value, including named strings, rgba() values, as well as many others.
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.

let points = []
for(let i = 0; i < numPoints; i++){
  let point = {x: canvas.width/numPoints*i, 
             y: canvas.height/2}
  points.push(point)
}
Enter fullscreen mode Exit fullscreen mode

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

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()
  })
Enter fullscreen mode Exit fullscreen mode

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

image

with 'hue' compositing
image

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

image
Same setup, longer run
image

Here is the codepen that generated it

Discussion (0)