Skip to content

Section 2: p5js!

Eric Beug edited this page Mar 1, 2019 · 11 revisions

Section 2: p5js! (FINALLY!)

Language Reference: https://p5js.org/reference <-- this is the most important resource for learning p5js. So important in fact, you will probably witness me having to look stuff up in here during this class!

Programming Examples: https://p5js.org/examples/

Tutorials: Daniel Shiffman wiling out on Youtube <-- more resources like this at the end of the syllabus

Section 2a: The Sketch

A long standing tradition in the "Processing" ecosystem is the idea that a program is called a "sketch"

A sketch consists of a few basic functions that almost always need to be present. The syntax is still just like javascript, but these conventions actually come from Processing which is based in JAVA (which for reasons no one can explan, has little or NOTHING to do with javascript). There is probably a joke to be made here about computer programmers either liking coffee or Indonesia.

IMAGE ALT TEXT HERE

When we load the p5js framwork into our DOM context, we "extend" javascript to do a bunch of new magical things for us. While we can still write normal javascript, we can also add a sketch (possibly multiple sketches) to the DOM and access some of the magic.

  1. To start let's create a new file, called anything you like (e.g. my_first_sketch.js) make sure you use the extension .js at the end of the file.

  2. almost every p5js sketch ever will contain these two functions (add them to your file and save):

function setup(){

}

function draw(){

}
    1. Now we need to tell our HTML file about this new script we've made:
<!DOCTYPE html>
<html>
  <head>
    <title>My First Sketch</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.15/p5.js"></script>
  </head>
  <body>

    <script src="my_first_sketch.js"></script>

  </body>
</html>

The function setup()

This code gets excecuted sequentially (line by line) exactly one time. When it has finished excecuting every line between the curly braces, it moves on to draw()

function setup(){
  //some code gets excecuted here
}

When it has finished excecuting every line between the curly braces, it moves on to draw() where it will stay (most of the time forever)

The function draw()

function draw(){
  //the bulk of your code gets executed here, rinse, repeat
  //the draw function loops continuously
}

Once draw is called, it excecutes the code inside the curly braces indefinitely.

Syntax & Structure

When we start a line with the word function we are indicating to javascript that what comes next is going to be a name of a function, followed by a set of commands to execute when that function is called. The syntax has a few components. function is the declaration, then it needs a name –functions can be named a seemingly infinite number of things, and we'll create and name our own functions in future sections– but setup() and draw() are kind of special. After the name, the next element is parentheses which is where normally you would supply arguments or parameters for the function to use, but in these two cases there are none – more on that later. And then finally curly braces to frame in the code that your function is going to execute.

declaration Name of function (arguments) {code to be executed}
function setup ([none]) code to be executed (once on startup)
function draw ([none]) code to be executed (looping indefinitely)

Section 2b: The Canvas

By default, a p5js sketch will add a canvas element the body of our HTML. Unfortunately it's not very generous with the canvas size and it defaults to 100 pixels x 100 pixels.

We can specify a few things in our setup function if we know them in advance. For example, if we know that we want our sketch to have a workspace of 640x480 pixels we can call the createCanvas(); function as a line of code during our setup function. Similar to setup() and draw(), createCanvas() is a function that was already defined in the p5js framework that we added into our html page. Unlike these previously discussed built-in functions, createCanvas() DOES take arguments and since it has already been defined in the p5js framework, instead of declaring it with the word function, we simply call it by putting it in our setup function.

Name of function ( arguments ) Example Description
createCanvas(); (width, height, renderer);

For [createCanvas();] width and height are required, the renderer is optional, but let's choose one and put it in for good measure. There canvas has two renderers available, P2D for 2-dimensional artwork, and WEBGL for 3-dimensional artwork. Let's start with P2D for now...

function setup(){
  createCanvas(640, 480, P2D);
}

function draw(){

}

Asking the browser to follow instructions

When we ask the browser to createCanvas(); we're explicitly asking it to complete this instruction. If we get it wrong, our sketch will not run correctly, and we should see an explanation of why in the console.

An example where we try to initialize createCanvas with things that p5js doesn't know about:

function setup(){
  createCanvas(PIZZA, HOTDOGS, HAMBURGERS);
}

function draw(){

}

error message:

> Uncaught ReferenceError: PIZZA is not defined
		at setup (section_3.js:2)
		at p5.<anonymous> (p5.js:44882)
		at p5.<anonymous> (p5.js:44810)
		at new p5 (p5.js:45103)
		at _globalInit (p5.js:46862)

another example, where we forget a closing parenthesis:

function setup(){
  createCanvas(640,480,P2D;
}

function draw(){

}

error message:

> Uncaught SyntaxError: missing ) after argument list

The semicolon is very important!

Notice that there is a semicolon at the end of the new line of code that we've just written. This is a subtle bit of syntax that is very important, as it tells the compiler/interpereter/browser, that we've come to the end of an instruction. Each line of instruction that we give computer should be ended with a semicolon. Unlike most languages, JavaScript will sometimes understand what you meant without a semicolon and let you get away with it without throwing an error, BUT in practice there should always be a semicolon at the end of a line inside or outside of a function block.

Bonus! draw a background to prove that we've properly instantiated a canvas:

If we run this code as is, it will be valid, but we won't really see anything different on the page. To test this visually pretty quickly, we can use another function like the one's we've seen so far that will fill the entire background background of our canvas with a color value so that we can distingush it from the HTML page:

Name of function (arguments) example Description
background(); (color) white:background(255); 24-bit color, 8-bit per channel + 8-bit alpha(transparency)

we could do this in either setup or our draw loop, and there are advantages to each. Let's try it in the draw loop for now...

function setup(){
  createCanvas(640, 480, P2D);
}

function draw(){
  background(255);
}

running this code should show our display our grey html background, and our new white canvas at the size we specified in the upper lefthand corner.

Try changing the numerical values in createCanvas(640, 480, P2D); and see what happens in the browser. We'll talk more about background(); in another section.

Section 2c: OMG can we plz draw something already!?!

There are a number of built in shape functions, let's start with an old favorite, the ellipse(); which, like all the other things we've seen so far in p5js is a function, and like createCanvas it has already been declared in our framework file and it also takes four arguments: x location, y location, width, and height.

Name of function ( arguments )
ellipse (x location, y location, width, height);
function setup(){
  createCanvas(640, 480, P2D);
}

function draw(){
  ellipse(200, 200, 100, 100);
}

Shape Primitives

There are many shape primitives in p5js, here are a few examples to start with:

Name of function ( arguments ) Example Description
ellipse(); (x, y, w, h) ellipse(50, 50, 100, 100); draw an ellipse on the canvas
rect(); (x, y, w, h) rect(50, 50, 100, 175); draw a rectangle on the canvas
line(); (x1, y1, x2, y2) line(10, 20, 30, 40); draw a line between to points on the canvas
triangle(); (x1,y1,x2,y2,x3,y3) triangle(125, 50, 50, 150, 200, 150); three points, line in between them and fill

Now is probably a good time to talk about Axis

So we've defined our canvas as 640 pixels across the horizontal axis and 480 pixels on the vertical axis. So now we have a grid of 307,200 pixels – each one with their own unique address. Just like in math class when you were a kid and learned about graphs with an x and y axis, our canvas is now like that, except 0,0 is the top left corner, and any number above zero on either brings us into the canvas. On the vice versa, negative numbers will move you off-screen 📺 (this can be handy sometimes).

mouseX / mouseY

Now that we've talked about axis, naturally we need to cut straight to mouse position. mouseX and mouseY are one of the niftiest and easiest little built-in interactivity tricks in p5js. They are just what they sound like – whenever your mouse moves on the canvas, p5 will get a callback from the browser that will return the position of the mouse. Unlike most of the other syntax we've seen in p5js thus far, mouseX and mouseY don't work like functions – they are more like variables that get updated by the browser at the speed of our sketch. We call them just by using their name, and they return a numeric value based on the Axis that we discussed in the previous section.

TODO: insert image

Section 2d: debugging with console.log()

In order to get a glimpse under the hood and see these numbers in action, we can use some of javascript's built-in debugging tools. The single most important debugging tool that you have at your fingertips is: console.log();

Adding console.log("something"); into our draw loop will print the word something over and over again in the console.

On a mac using chrome:

  • press option + command + j
  • -->View -->Developer -->JavaScript Console

On a mac using safari:

  • press option + command + c
  • -->Develop -->Show JavaScript Console

(you may have to enable the developer preferences in the advanced section)

we can get some insight on how this works by adding console.log("mouseX = " + mouseX + " | mouseY = " +mouseY); to our draw loop. Now open the javascript console/debiugger and see what happens when you move the mouse around on the page.

you should see output like this:

> mouseX = 361 | mouseY = 205 .... section_3.js:7

> mouseX = 362 | mouseY = 211 .... section_3.js:7

> mouseX = 362 | mouseY = 216 .... section_3.js:7

> mouseX = 362 | mouseY = 218 .... section_3.js:7

> mouseX = 362 | mouseY = 219 .... section_3.js:7

> mouseX = 362 | mouseY = 220 .... section_3.js:7

Section 2e: Bare Bones Interactivity!

Now for for our first foray into an interactive sketch. You can remove the console.log() line now that we've seen what our program is doing when we print mouseX and mouseY.

Since the first two arguments of our ellipse(x, y, width, height), are the x and y axis locations that the sketch will draw our ellipse, if we update those to be ellipse(mouseX, mouseY, 100, 100); our ellipse should be redrawn wherever the mouse goes.

your sketch should look something like this:

function setup(){
  createCanvas(640, 480, P2D);
}

function draw(){
  ellipse(mouseX, mouseY, 100, 100);
}

It is time to talk about the background(); again

Since now we've got a sketch that makes it seem like we're high on drawing snakes, we should reconsider the background();. Drawing the background is purely optional – there are reasons you might want to call it during the setup, other times it is usefull in the draw loop.

While we're here we should witness the full power of the background, by utilizing all of it's arguments: background(R, G, B, ALPHA);

Give it a try:

function setup(){
  createCanvas(640, 480, P2D);
}

function draw(){
  background(255,0, 200, 255);   
  ellipse(mouseX, mouseY, 100, 100);
}

Cheap Tricks

A very cheap but useful trick is to use the alpha channel to only partially draw the background. See what happens when you reduce the last value in the background function we described to 20...

function setup(){
  createCanvas(640, 480, P2D);
}

function draw(){
  background(255,0, 200, 20);   
  ellipse(mouseX, mouseY, 100, 100);
}

Section 2f Let's make some maths!

Basic arithmetic operations:

  • + addition
  • - subtraction
  • * multiplication
  • / division
  • % modulo (what is the remainder if n is divided by x)

Shorthand math

  • ++ add one shorthand
  • -- subtract one shorthand
  • += shorthand for add n to x
  • -= shorthand for subtract n from x

If we go back to our console.log() tool, we can use JavaScript to compute some things for us. Try telling computer to maths in your draw loop!

console.log(2017-1983);

> 34

console.log(2*3*4-5*3)

> 9

Make computer do maths that is more relevant to our sketch

This is cool because we can employ math to calculate things that we're too lazy to do on our own (Pro Tip: this is most of the motivation behind most of computer programming). Let's start with something simple: center a shape on the the canvas using math.

Similar to mouseX and mouseY we have some other built-in variables at our disposal: width and height this is comes for free when we define our canvas object in the setup scope. We have access to these now, and we can do some math with them:

function setup(){
  createCanvas(640, 480, P2D);
}

function draw(){
  background(255,0, 200, 20);   
  ellipse(mouseX, mouseY, 100, 100);
  ellipse(width/2, height/2, 200, 200);
}

If we divide the width and height of our canvas AND we use that value in place of the location attributes our ellipse will be centered on the screen.

With ellipse(), the location arguments define the center of the ellipse. This is cool, but it breaks down with rect() because unlike ellipse() the x and y values define the top left corner of rect.

function setup(){
  createCanvas(640, 480, P2D);
}

function draw(){
  background(255,0, 200, 20);   
  ellipse(mouseX, mouseY, 100, 100);
  rect(width/2, height/2, 178, 178);
}

We can get around this problem with math! The math is slightly more complex than before, but what do we care – we don't have to do it. The computer does! In this case, we've given our rect a width of 178 and a height of 178. So, what we want is to subtract half of each of those numbers from the halves of our canvas.

function setup(){
  createCanvas(640, 480, P2D);
}

function draw(){
  background(255,0, 200, 20);   
  ellipse(mouseX, mouseY, 100, 100);
  rect(width/2-178/2, height/2-178/2, 178, 178);
}

Section 2g Shape Settings

These are a few more functions that allow you to control the fill color, the color outline and the thickness of the outline. p5js gives us a multitude of ways to work with color and colorModes. For simplicity I'm only outlining one method – RGB[A]

Name of function ( arguments ) Example Description
fill(); (v1,v2,v3,[alpha]) fill(255,0,255,127); pink with 50% transparency 24-bit color, 8-bits per channel, Red, Green, Blue, Alpha (transparency) – the last value is optional
stroke(); (v1,v2,v3,[alpha]) stroke(0);
noFill(); none the same as using fill and setting the alpha to 0
noStroke(); none similar to using stroke and setting the alpha to 0
strokeWeight(); number strokeWeight(4); any positive interger will determine the width of the outline in pixels

Note that when you apply one of these settings to our draw loop it will affect every other shape in the draw loop regardless of where it is. While that can be convenient if you want all your shapes to be the same color and have the same aspects of outline, you might not always want that. That's where push(); and pop(); come in!

Section 2 Bonus:push(); and pop();

Sort of similar to the way our HTML file uses tags like <body> content </body>, push(); and pop(); are like beginnings and endings to any p5js specific style attributes that we change. The unusual thing about these two built-in functions is that they have to be used together (one does nothing without the other). In p5js, push(); marks the beginning, whereas pop(); delineates the end of our styling.

function setup(){
  createCanvas(640, 480, P2D);
}

function draw(){
  background(255,0, 200, 20);

  push();  
    fill(0,0,255);
    stroke(0);
    strokeWeight(10);
    rect(50,50,50,50);
  pop();
   
  push();    
    noFill();
    stroke(200,255,0);
    strokeWeight(20);
    ellipse(mouseX, mouseY, 100, 100);
  pop();

}

This usage of push() and pop() is similar to how function "scope" works in javascript, which is what we'll talk about in the next section.