Animation using p5.js

Authored by John Kuiphoff


Animation in p5.js works kind of like a traditional flip book animation. The draw() function gets called approximately 60 times a second, each time drawing an image onto the screen. The illusion of movement is achieved by drawing a sequence of static images that minimally differ from each other.

In the previous tutorial, we learned how to draw simple shapes using P5.js using code. The ellipse that was drawn on the screen was actually being drawn 60 times a second in the same spot.

How could we make a flip book animation with the same ellipse using p5.js? We could draw an ellipse on the screen and then draw another ellipse slightly to the right of the previous one on the next frame. And do that a few hundred times... and it would appear to animate something like this:

This concept might actually be a little easier to understand if we draw a semi-transparent rectangle on the screen (instead of painting the background white each time the frame is drawn) so that you can see the previous frames:

Variables

How can we draw the circle in a new position on each frame? To do this we are going to use a variable. Programmers love variables. A variable is a value that might change over time. In real life, the outside temperature could be considered a variable. It has a value that might change over time. If I check the temperature in the morning, it might be different if I check it again in the afternoon.

We will use a variable to change the x position of the circle for each frame to simulate motion. So, on the first frame the xpos of the circle will be zero; on the next frame we will make the xpos of the circle equal to one; on the frame after that, we will make it three... and so on.

// x position variable
var xpos = 0;

function setup() 
{
  // set canvas size
  createCanvas(400, 200);
}

function draw() 
{
  // clear background
  background(255);

  // set the fill color
  fill(255, 0, 0);

  // black outline
  stroke(0);

  // set the ellipse mode
  ellipseMode(CENTER);

  // increment x variable
  xpos = xpos + 2;

  // if the circle moves off screen, reset it's position
  if(xpos > width)
  {
    xpos = 0;
  }

  // draw a circle
  ellipse(xpos, 100, 25, 25);	

  // display xpos variable
  fill(0);
  text("xpos = " + xpos, 25, 25);
}

The first line declares a new variable called 'xpos': var xpos = 0;

When you first declare a new variable, you will need to put the 'var' keyword in front of the variable name.

Our program could easily change the value of 'xpos' by assigning a new value to it. For example: xpos = 67; would make the value of xpos equal to 67. I could also get the value of the variable to print in the console of the web browser by: print(xpos);

Again, the reason that we need this variable is because we need a value that can change over time (to simulate animation). The way that our program works is that on every frame, we increment the value of xpos by two (pixels): xpos = xpos + 2;

Then, we draw an ellipse on the screen based on the value of xpos: ellipse(xpos, 100, 25, 25);

Conditionals

There is also a few other lines of code in the previous example worth noting:

// if the circle moves off screen, reset it's position
if(xpos > width)
{
  xpos = 0;
}

When programming, we often need to ask the computer a few questions (using a conditional). In this case, we are asking the computer: "Is the value of xpos greater than the width of our sketch?" If the answer is yes, the computer is instructed to set the value of xpos equal to zero. If we didn't do this, the value of xpos would move forever and the circle would be in outerspace or something - definitely not visible on the screen anymore. This code will keep the circle on the screen at all times. We will talk more about conditionals later in the course notes.

Instead of resetting the position of the circle, how can we make the circle bounce when it reaches the edge of the screen? First, we will need to know if the circle has reach the left or right edge. If it has, we can tell the circle to reverse it's direction by multiplying the 'speed' variable by -1. When the speed variable is set to a number greater than zero, the circle will move right. When the speed variable is set to a negative number, the circle will move left.

// x position variable
var xpos = 0;

// speed variable
var speed = 2;

function setup() 
{
  // set canvas size
  createCanvas(400, 200);
}

function draw() 
{
  // clear background
  background(255);

  // set the fill color
  fill(255, 0, 0);

  // black outline
  stroke(0);

  // set the ellipse mode
  ellipseMode(CENTER);

  // increment x variable
  xpos = xpos + speed;

  // if the circle moves off screen, change the speed's polarity
  if((xpos > width) || (xpos < 0))
  {
    speed = speed * -1;
  }

  // draw a circle
  ellipse(xpos, 100, 25, 25);	

  // display xpos variable
  fill(0);
  text("xpos = " + xpos, 25, 25);
}

Now that we have a circle that bounces back and forth, what can you do to make this example more interesting? Try changing this example so that each time the circle hits a side of the sketch window, the fill color of the circle changes to a random color. To do this, you may need to look at the documentation for creating a color variable as well as creating random numbers.

Keyboard Events

All of the examples that we have covered just do their own thing. How can we begin to make our sketches more interactive? For example, how could we make the circle respond to keyboard events or mouse events? Well, it's actually not all that difficult - let's start with a simple keyboard event. In the example below, the circle will move to a random location each time a key on the keyboard is pressed (you may need to click on the sketch one time to give it focus):

// x position variable
var xpos = 200;

// y position variable
var ypos = 100;

function setup() 
{
  // set canvas size
  createCanvas(400, 200);
}

function draw() 
{
  // clear background
  background(255);

  // set the fill color
  fill(255, 0, 0);

  // black outline
  stroke(0);

  // set the ellipse mode
  ellipseMode(CENTER);

  // draw a circle
  ellipse(xpos, ypos, 25, 25);	
}

function keyPressed()
{
  xpos = random(0, width);
  ypos = random(0, height);
}

Great - we just built a really boring video game. We will change the code in a little bit to make a keyboard controlled character that responds to arrow keys. That'll make it slightly better than before. But first, let's just review what just happened in this example. We have two variables: xpos and ypos. When a user presses any key, the keyPressed() function is called. This function is native to p5.js and gets called anytime a key is pressed - the documentation for it can be found here: http://p5js.org/reference/#/p5/keyPressed. The code that we placed inside of this function simply creates a random number between zero and the width of the screen (400 - according to our createCanvas() function) and assigns that random number to the xpos variable. We do something very similar for the ypos variable. These variables are used in the draw() function when the ellipse is drawn.

Now onto something a little bit more advanced. In the example below, we now check to see which arrow key is pressed:

// x position variable
var xpos = 200;

// y position variable
var ypos = 100;

// numPixels variable
var numPixels = 10;

function setup() 
{
  // set canvas size
  createCanvas(400, 200);
}

function draw() 
{
  // clear background
  background(255);

  // set the fill color
  fill(255, 0, 0);

  // black outline
  stroke(0);

  // set the ellipse mode
  ellipseMode(CENTER);

  // draw a circle
  ellipse(xpos, ypos, 25, 25);	
}

function keyPressed()
{
  // UP key
  if(keyCode == UP_ARROW)
  {
    ypos = ypos - numPixels; 
  }

  // DOWN key
  if(keyCode == DOWN_ARROW)
  { 
    ypos = ypos + numPixels; 
  }

  // RIGHT key
  if(keyCode == RIGHT_ARROW)
  {
    xpos = xpos + numPixels; 
  }

  // LEFT key
  if(keyCode == LEFT_ARROW)
  {
    xpos = xpos - numPixels; 
  }
}

To learn more about keyboard events, take a look at the p5.js documentation: http://p5js.org/reference/#group-Input.

Great! So we have a simple character that we can move around. Now let's try something a little bit more difficult. Let's add easing and fluid movement so that the character 'glides' across the sketch. To help with this, we can take a look at this code made in Processing and see if you can refactor it to work in p5.js with keyboard events: https://www.processing.org/examples/easing.html. Programmers often spend a lot of time looking at existing (open source) code and changing it to suit their own purposes.

// x position variable
var xpos = 200;

// y position variable
var ypos = 100;

// numPixels variable
var numPixels = 50;

// target x and y positions
var targetX;
var targetY;

// easing variable
var easing = 0.05;

function setup() 
{
  // set canvas size
  createCanvas(400, 200);
}

function draw() 
{
  // clear background
  background(255);

  // set the fill color
  fill(255, 0, 0);

  // black outline
  stroke(0);

  // set the ellipse mode
  ellipseMode(CENTER);
  
  // calculate the new xpos value
  var dx = targetX - xpos;
  if(abs(dx) > 1) {
    xpos += dx * easing;
  }
  
  // calculate the new ypos value
  var dy = targetY - ypos;
  if(abs(dy) > 1) {
    ypos += dy * easing;
  }

  // draw a circle
  ellipse(xpos, ypos, 25, 25);	

}

function keyPressed()
{
  // UP key
  if(keyCode == UP_ARROW)
  {
    targetY = ypos - numPixels; 
  }

  // DOWN key
  if(keyCode == DOWN_ARROW)
  { 
    targetY = ypos + numPixels; 
  }

  // RIGHT key
  if(keyCode == RIGHT_ARROW)
  {
    targetX = xpos + numPixels; 
  }

  // LEFT key
  if(keyCode == LEFT_ARROW)
  {
    targetX = xpos - numPixels; 
  }
}

In the last example, we were using the arrow keys to control a character using something like: if(keyCode == LEFT_ARROW). However, if you'd like to capture normal keys like the letter 'R', you need to use the following: if(key == 'R' || key == 'r'). In the example below, try pressing the R, G, or B keys to change the background color of the sketch:

// color variable
var c;

function setup() 
{
  // set canvas size
  createCanvas(400, 200);
  
  // set default background color
  c = color(255, 255, 255);
}

function draw() 
{
  // clear background
  background(c);

  // instructions
  fill(0);
  text('Press the R, G or B key to change the background color', 25, 25);	

}

function keyPressed()
{
  // R key
  if(key == 'R' || key == 'r')
  {
    c = color(255, 0, 0);
  }

  // G key
  if(key == 'G' || key == 'g')
  {
    c = color(0, 255, 0);
  }
  
  // B key
  if(key == 'B' || key == 'b')
  {
    c = color(0, 0, 255);
  }
}

Mouse Events

Working with mouse events using p5js is easy. We can create sketches that do various things based on the mouse's position on the screen or when the mouse button is pressed, etc. Here is a list of the mouse related functions in p5js:

Let's start things off by creating a sketch that simply tracks the location of mouse. Also, when the mouse button is pressed, let's create a random background color. The variables mouseX and mouseY are variables that we can always refer to in p5js. P5js gives us these variables for free - we don't need to create them ourselves. Also, to detect when the mouse button has been pressed, we can use the mousePressed() event handler.

// color variable
var c;

function setup() 
{
  // set canvas size
  createCanvas(400, 200);
  
  // set default background color
  c = color(255, 255, 255);
}
function draw() 
{
  // clear background
  background(c);

  // mouse coordinates
  fill(0);
  text('X: ' + mouseX + ' Y: ' + mouseY, 25, 25);	

}

function mousePressed()
{
  // create a random background color
  c = color(random(255), random(255), random(255));
}

You can also use touch events on your mobile device using p5.js (only works on a phone or a tablet):

Launch demo using a mobile device
// color variable
var c;

function setup() 
{
  // set canvas size
  createCanvas(400, 200);
  
  // set default background color
  c = color(255, 255, 255);
}
function draw() 
{
  // clear background
  background(c);

  // touch coordiantes
  fill(0);
  text('X: ' + touchX + ' Y: ' + touchY, 25, 25);	

}

function touchStarted()
{
  // create a random background color
  c = color(random(255), random(255), random(255));
}
comments powered by Disqus