animation with p5js
Here's a page on the p5js.org website that introduces animation.
https://p5js.org/examples/hello-p5-animation.html
Let's understand how it works.
One of the first things you might not understand is what the variables width and height mean. Have a look at these pages:
https://p5js.org/reference/#/p5/width
https://p5js.org/reference/#/p5/height
The reason that p5js can animate is the draw function, which by default loops at a frame rate of 60 frames per second. What does the frame rate of an animation mean? This page on Wikipedia explains frame rate:
https://en.wikipedia.org/wiki/Frame_rate
You also know that if you put this code in the setup function, the animation will stop:
noLoop()
an example to work with
So let's simplify and then change the code from the p5js site above so that we can understand animation better.
let x, y; function setup() { createCanvas(720, 400); // Starts in the middle x = width / 2; y = height; } function draw() { background(200); // Draw a circle stroke(50); fill(100); ellipse(x, y, 24, 24); // Moving up at a constant speed y = y - 1; // Reset to the bottom if (y < 0) { y = height; } }
In this code, on each frame, the value of y changes. It is reduced by one. Because of the if statement, when the value of y is less than 0, it is reset to the value of height.
using framecount
What if we want the ball to move up and down within the page, though? To do that, first I think we should understand something about frameCount.
A p5js variable called frameCount will tell us which frame we are on at the moment.
https://p5js.org/reference/#/p5/frameCount
Let's add the frameCount to the middle of the window so that we can see it increasing.
let x, y; function setup() { createCanvas(720, 400); // Starts in the middle x = width / 2; y = height; textSize(30); textAlign(CENTER); } function draw() { background(200); // Draw a circle stroke(50); fill(100); ellipse(x, y, 24, 24); // Moving up at a constant speed y = y - 1; // Reset to the bottom if (y < 0) { y = height; } text(frameCount, (width / 2) - 50, height / 2); }
another way to loop
Remember that we studied the remainder operator before; it divides a number and gives us the remainder. We can use it to loop numbers within a range. Look just past the middle of this page for a review.
https://renickbell.net/middleschool/doku.php?id=core:p5js-push&s[]=%2Aremainder%2A
We can use the remainder operator with the frameCount to change the value of x. Add this line to the code above on line 28 between the code for y and the code for the text.
x = frameCount % width
Now you should see the ball moving from the bottom left corner toward the top. Every time the ball reaches the right side of the screen, it will return to the left side.
going back and forth
That's kind of interesting, but what if we want the ball to go back and forth from left to right instead of going off the screen and reappearing on the opposite side? There's a useful function in math called the sine function. It's part of the area of math that we call trigonometry. You're going to study it soon in math class more deeply, but let's go ahead and start using it for our animation.
You can find some good basic information about sine here:
https://en.wikipedia.org/wiki/Sine
The important thing that we want to know about the sine function right now is that it accepts one value and always returns a value between -1 and 1. We can call it in JavaScript like this:
Math.sin(0)
Unlike Math.random, which gives us random values, Math.sin will move in order from -1 to 1 and then back. Let's have a look at it. In the code below, we divide the framecount by 100 and then get the sine of it. Watch how the number changes as the frameCount increases.
let x, y; function setup() { createCanvas(720, 400); // Starts in the middle x = width / 2; y = height; textSize(20); textAlign(LEFT); } function draw() { background(200); text(frameCount, 50, height / 2); text("this is the Math.sin of frameCount divided by 100: " + Math.sin(frameCount/100), 50, 50 + height / 2); }
Notice that the value of Math.sin is always between -1 and 1. You might also notice that it slows down a little as it gets close to either -1 or 1.
Now try going back to the code with the ball and change the value of x to this:
x = Math.sin(frameCount/100) * width
bouncing off the side
Notice now that the ball slows down as it gets to the right side of the screen, but when it goes to the left side of the screen, it seems to be gone for a long time before it comes back. Why is that? It's because Math.sin gives us a range between -1 and 1. That means that sometimes the value of x is going from 0 to negative width, which is far off the left side of the screen. Here's a picture that might help you understand what is going on.
You might think that we can just prevent it from becoming negative. Math also has a standard function that takes any number and returns the positive version. That's called the absolute value. JavaScript has a function for that. We can use it like below. Try each of these:
Math.abs(-100) Math.abs(23) Math.abs(-23)
So why don't we just try that on our x value above?
x = Math.abs(Math.sin(frameCount/100) * width)
Here's a full example with a few other things adjusted to make it easier to see.
let x, y; function setup() { createCanvas(720, 400); // Starts in the middle x = width / 2; y = height; textSize(20); textAlign(LEFT); } function draw() { background(200); // Draw a circle stroke(50); fill(100); ellipse(x, y, 24, 24); // Moving up at a constant speed y = y - 1; // Reset to the bottom if (y < 0) { y = height; } noStroke(); text(frameCount, 50, height / 2); text("this is the Math.sin of frameCount divided by 100: " + Math.abs(Math.sin(frameCount/100)), 50, 50 + height / 2); x = Math.abs(Math.sin(frameCount/100) * width) }
Well, that kind of works, but now it looks like it's bouncing every time it hits the left side of the screen. What if we want it to smoothly float from left to right? The trick is that we need to move the whole animation to the right. Without Math.abs, the animation is doing something like this:
To fix this, we need to do two things.
- reduce the range by half
- move the whole animation to the middle of the screen
We can do that by using this code for the value of x:
x = width/2 + (Math.sin(frameCount/100) * width/2);
Now it should be moving back and forth between the left and right edges while snaking up the screen.
changing the speed
The speed is controlled by the 100 in this line:
x = width/2 + (Math.sin(frameCount/100) * width/2);
If you make the number smaller, the ball will move faster. Try it.
x = width/2 + (Math.sin(frameCount/10) * width/2);
In the same way, if you make the number bigger, it will get slower. Try that.
x = width/2 + (Math.sin(frameCount/1000) * width/2);
If you change these, don't forget to change the code in the text, too, so you can see the number changing in the same way as the ball is moving.
a complete example
You might just want to see the ball float from left to right without going up and down. Here's the complete code for that, along with some animated text to help you understand what is going on.
let x, y; function setup() { createCanvas(720, 400); // Starts in the middle x = width / 2; y = height; textSize(30); textAlign(LEFT); } function draw() { background(200); // Draw a circle stroke(50); fill(100); ellipse(x, y, 24, 24); y = 150; // this stops the ball from going up //y = y -1; x = width/2 + (Math.sin(frameCount/100) * width/2); noStroke(); text("this is the frameCount: " + frameCount, 50, height / 2); textSize(20); text("this is the Math.sin of frameCount divided by 100: " + Math.sin(frameCount/100), 50, 25 + height / 2); text("this is the value of x: " + x, 50, 50 + height / 2); }
an exercise
Go back to the first example above. Can you make it go down instead of up? How about from left to right and then restart again on the left side?