Enjoy sketching your ideas with p5.js

Datetime:2016-08-23 03:01:18          Topic:          Share

On the past few weeks I started to watch a serie of tutorials of Daniel Shiffman about genetic algorithms, and I could see that all the examples were developed on something called p5.js . I had never heard of p5.js before, but it looked like easy and really funny to use (besides the fact that Shiffman is a very funny guy).

So I said to myself: I need to try this! And after a couple of days playing with it, I would like to show here a couple of basic examples to start with p5.js .

First of all... what is exactly p5.js?

p5.js is a Javascript library that is intended to achieve something similar to Processing , this is: make accesible and comfortable to artists, teachers, designers... to translate into code all their ideas. Has a lot of drawing functionalities, so you can treat your HTML file as a blank canvas to sketch anything.

But it's possible to accomplish more than simply draw some shapes by adding different libraries , so you can interact with other elements such as your webcam, sounds, forms...

Also, p5.js is totally independent, so you don't need to add to your project other libraries such jQuery to do all the cool stuff.

Some basic concepts to have in mind

The complete functionality of p5.js relies in only three files you can download here . That's it, nothing more. So the first step to make something is download p5.js and create some basic HTML structure to load the Javascript files. Something like this:

<!DOCTYPE html>  
<html>  
  <head>
    <meta charset="utf-8">
    <title>p5.js example</title>
  </head>
  <body>
    <div id="canvas"></div>

    <script src="p5.min.js"></script>
    <script src="addons/p5.dom.js"></script>
    <script src="addons/p5.sound.js"></script>
  </body>
</html>

In all the p5.js sketches, there are two main methods to have in mind in order to do whatever you want: setup() and draw() .

The setup() method is called at the start of the process, and his mission is to initialize all the information and elements that will be needed such canvas, fonts, images... There can only be one setup() method defined and it shouldn't be called again after its initial execution. You can read a more detailed documentation about this method here .

Meanwhile, the draw() method continuously executes the lines of code contained on it, and as his name suggest, is intended to present on the screen all the elements we want to show. Is called directly after setup() . The number of times draw() executes in each second may be controlled with the frameRate() function (by default this value is 60 times per second, but you can change it). You can read a more detailed documentation about this method here .

So, with this two main methods we can write our very first and simple sketch with p5.js that may looks like this:

function setup() {  
  // Some cool code
}

function draw() {  
  // Some even more cool code
  rect(10, 10, 40, 40);
}

This piece of code, as you might suppose, will draw a rectangle on the coordinates (10, 10) with a width and height of 40 pixels (more details about rect() method here ).

Let's start with a very simple example

Oooook, so now we're ready to go further and try something a little bit more fancy. For example, create a black canvas with 20 random circles. Super simple, but it will help us to combine the concepts seen previously.

Making use of the above HTML code, we're going to use the setup() method to initialize a black canvas, assign it to the <div id="canvas"></div> element, and with the help of the random() and ellipse() methods, create 20 white circles. Let's create a sketch.js with this piece of code:

function setup() {
    // Create the canvas
    var canvas = createCanvas(720, 400);
    canvas.parent('canvas');

    // Set canvas background color to black
    background(0);

    // Set fill circle color to white
    fill(255);

    // Create 20 random circles to draw
    for(var i = 0; i < 20; i++) {
      var rx = random(720) + 1;
      var ry = random(400) + 1;
      ellipse(rx, ry, 20, 20);
    }
  }

Then load this sketch.js in your HTML like:

<script src="p5.min.js"></script>
  <script src="addons/p5.dom.js"></script>
  <script src="addons/p5.sound.js"></script>
  <!-- OUR MAGIC SCRIPT -->
  <script src="sketch.js"></script>

and if you open the HTML file, you will see something like this:

There're some new methods involved here:

  • createCanvas() will create a HTML canvas element in the document, and will set the dimensions on pixels. With the help of the .parent() method we can specify the HTML element that we want to be the parent of this new canvas, in our case, the <div id="canvas"></div> element (more info about createCanvas() here ).

  • background() will apply a background color to the generated canvas (more info about background() here ).

  • fill() will apply a fill color to the shapes that we generate after the execution (more info about fill() here ).

  • ellipse() will draw an ellipse on the coordinates (rx, ry) with a width and height of 20 pixels (more info about ellipse() here ).

So we're done with our very first sketch on p5.js ... Yeeah! :D

Ok, but I want to do something more complex... The T-Rex running game!

In one of his latest videos, Shiffman has developed simplified versions of two popular games (agar.io and Flappy Bird) with p5.js . Always using simple shapes such as circles, rectangles... to represent the graphical elements. I was amazed by how easy is to develop a mini-game in a matter of minutes with p5.js . So inspired by this Flappy Bird version, and taking this as a starting point, I began to make my own version of another game that surely you know: the Google Chrome's T-Rex game.

As you can see, it's quite simple, only two elements on the screen (the player and the obstacles), and with the only aim of dodge all the obstacles that appear on our way by pressing one key.

To accomplish this, we'll need to define some concepts, like player and obstacle , that will be two models in our project. As we're going to represent both concepts only with shapes, we'll add a third element to bring a little bit of spice to the example: the star object, so we can draw some "stars" on the background just for fun.

In order to keep certain structure on the project, we're going to create individual files for each of the objects. To try not to make this post too long, I'll show the final code of each object and I'll explain the most important elements so they can be understood correctly.

Anyway, as you can see, due to the fact that p5.js is very intuitive to use, the code is quite affordable to understand by itself. You can find all the documentation about the methods used in this example here . So without further adieu... let's go!

player.js

We're going to use a white square to display the player. So we have to define some properties, such as the size of the square, the x and y values to position the player on the canvas, and his score . To simulate the jump, we need also to define the value of the gravity applied to the player, as well as the velocity and the jump_height .

And for the behavior, we only need to define five basic methods: show() , update() , onBottom() , jump() and hits() .

  • show() will display the square of the player using the rect() method. It will also show in the upper left corner of the player's score.

  • update() will change the value of y depending on the position of the player (if the position of the player is greater than the height of the canvas, we don't update the value of y to keep the player on "the floor" of the canvas).

  • onBottom() will check if the player is on "the floor" of the canvas in order to allow or not to jump (if the player is in the middle of one jump, we don't want to allow to jump again until the player falls again into the floor).

  • jump() is responsible of change the speed applied to the player to modify the final value of y and simulate the jump.

  • hits(obstacle) will check if the player hits some obstacle looking at the x and y values of the player and the obstacle.

Here is the code for the player object:

function Player() {  
  this.size = 30;
  this.y = height - this.size;
  this.x = 48;

  this.gravity = 0.98;
  this.velocity = 0;
  this.jump_height = 16;
  this.score = 0;

  this.show = function() {
    fill(255);
    rect(this.x, this.y, this.size, this.size);

    textSize(14);
    text("SCORE: " + this.score, 20, 30);
  }

  this.update = function() {
    this.velocity += this.gravity;
    this.y += this.velocity;

    if((this.y + this.size) > height) {
      this.y = height - this.size;
      this.velocity = 0;
    }
  }

  this.onBottom = function() {
    return this.y == (height - this.size);
  }

  this.jump = function() {
    this.velocity -= this.jump_height;
  }

  this.hits = function(obstacle) {
    if((obstacle.x >= this.x) && (obstacle.x <= (this.x + this.size)) &&
       ((this.y + this.size) >= (height - obstacle.height))) {
      return true;
    } else {
      return false;
    }
  }
}

obstacle.js

For the obstacles, we're going to use a gray rectangle as visual representation. We have to define some properties, such as the the x value to position the obstacle on the canvas, the width and height , and the speed with which move across the screen.

For the behavior, in this case we only need to define three basic methods: show() , update() and outOfScreen() .

  • show() will display the rectangle of the obstacle using the rect() method.

  • update() will change the value of x depending on the speed of the obstacle.

  • outOfScreen() will check if the obstacle moves out of the screen, so we can destroy the element to avoid store unnecessary elements on memory.

Here is the code for the obstacle object:

function Obstacle() {  
  this.x = width;
  this.height = random(80) + 20;
  this.width = 30;
  this.speed = 6;

  this.show = function() {
    fill(192);
    rect(this.x, (height - this.height), this.width, this.height);
  }

  this.update = function() {
    this.x -= this.speed;
  }

  this.outOfScreen = function() {
    return (this.x < -this.width ? true : false);
  }
}

star.js

Finally, for the stars we're going to use little ellipses as visual representation. We have to define the x and y values to position the star on the canvas, and the speed with which move across the screen.

For the behavior, the star will act almost the same as the obstacle, so we need to define: show() , update() and outOfScreen() .

  • show() will display the ellipse of the star using the ellipse() method.

  • update() will change the value of x depending on the speed of the star.

  • outOfScreen() will check if the star moves out of the screen, so we can destroy the element to avoid store unnecessary elements on memory.

Here is the code for the star object:

function Star() {  
  this.x = width;
  this.y = random(300);
  this.speed = 2;

  this.show = function() {
    fill(255);
    ellipse(this.x, this.y, 3, 3);
  }

  this.update = function() {
    this.x -= this.speed;
  }

  this.outOfScreen = function() {
    return (this.x < -this.width ? true : false);
  }
}

sketch.js

Once we have our files with the code of all the elements, we can create the script that will gather all and implement the game mechanics.

First of all, we have to define three global variables: player will store the instance of the player, obstacles as an array of instances of obstacles, and stars as an array of instances of stars.

Then we're going to use three special methods of p5.js to handle the gameplay: the aforementioned setup() and draw() methods, along with keyPressed() . We'll add a fourth method that will be the one responsible for initializing all elements: initialize() .

  • setup() will invoke the initialize() method to start the game.

  • initialize() is the responsible of initialize the value of the global variables with fresh instances of our three elements, as well as to create the game's canvas.

  • draw() is where the magic happens. As we have seen before, this method runs continuously at a rate of 60 times per second by default, so this is where we need to focus the control of the game (draw all the elements on the canvas, update the state of the items, create new items if necessary ...).

  • keyPressed is called when the user press some key. Here we're going to check if the key pressed is the one we have defined to trigger the jump (in my case I used the 'W' key).

Here is the code for the final sketch of the game:

var player;  
var obstacles = [];  
var stars = [];

function setup() {  
  initialize();
}

function draw() {  
  background(0);

  // Loop through the obstacles array
  for(var i = obstacles.length - 1; i >= 0; i--) {
    // Update the state of the obstacle
    obstacles[i].show();
    obstacles[i].update();

    // If the player hits the obstacle, we reset the game
    if(player.hits(obstacles[i])) {
      initialize();
      return false;
    }

    // If the obstacle goes out of the screen, we remove it from the obstacles array and
    // increments the amount of points of the player
    if(obstacles[i].outOfScreen()) {
      obstacles.splice(i, 1);
      player.score += 10;
    }
  }

  // Loop through the stars array
  for(var i = stars.length - 1; i >= 0; i--) {
    // Update the state of the star
    stars[i].show();
    stars[i].update();

    // If the star goes out of the screen, we remove it from the stars array
    if(stars[i].outOfScreen()) {
      stars.splice(i, 1);
    }
  }

  // Update the state of the player
  player.update();
  player.show();

  // Every 80 frames, we spawn a new obstacle
  if(frameCount % 80 == 0) {
    obstacles.push(new Obstacle());
  }

  // Every 10 frames, we spawn a new star
  if(frameCount % 10 == 0) {
    stars.push(new Star());
  }
}

function keyPressed() {  
  if(keyCode == 87 && player.onBottom()) {
    player.jump();
  }
}

function initialize() {  
  obstacles = [];
  stars = [];

  var canvas = createCanvas(720, 400);
  canvas.parent('canvas');

  player = new Player();
  obstacles.push(new Obstacle());
  stars.push(new Star());
}

Note: Tell me in the comments or via message if you need to go into detail on this code if there is anything you don't understand, I have not done to avoid excessively increasing the post.

Done all this code, the last step is to load all this files on our HTML like:

<script src="p5.min.js"></script>
  <script src="addons/p5.dom.js"></script>
  <script src="addons/p5.sound.js"></script>
  <!-- OUR MAGIC SCRIPTS -->
  <script src="player.js"></script>
  <script src="obstacle.js"></script>
  <script src="star.js"></script>
  <script src="sketch.js"></script>

and after all this effort, if we open the HTML file, you should see something like this:

Oh my gosh... it works! :_D

Any place where I can see the result?

Yeah! Here you have my list of sketches (right now I only have two of them), the TRex example , and the Github repo for this example, so you can clone it, change it, play with it... whatever you want! :)