Skip to content

p5.js overview

Kenneth Lim edited this page Dec 15, 2021 · 31 revisions

p5.js is a JavaScript library that starts with the original goal of Processing—to make coding accessible for artists, designers, educators, and beginners—and reinterprets this for today's web.

Using the original metaphor of a software sketchbook, p5.js has a full set of drawing functionality. However, you're not limited to your drawing canvas, you can think of your whole browser page as your sketch! For this, p5.js has addon libraries that make it easy to interact with other HTML5 objects, including text, input, video, webcam, and sound.

p5.js is a new interpretation, not an emulation or port, and it is in active development. You can also read how this differs from processing.js, or about how this relates to Processing.

Hello world

Please see the get started page for instructions for setting up your editing environment and project.

There are two main functions you will use in your program. The setup() block runs once, and is typically used for initialization, or for creating a program that does not need a loop running code repeatedly. The draw() block runs repeatedly, and is used for animation.

For your first "Hello World" program, create a setup() block and add one line:

function setup() {
  line(15, 25, 70, 90);
}

If you view this in your browser, you should see a single line.

Adding a draw() block enables you to incorporate animation. In the example below, a variable is updated every time draw() runs, moving a circle across the screen.

let x = 0;

function setup() {
  background(100);  
}

function draw() {
  ellipse(x, height/2, 20, 20);
  x = x + 1;
}

There is a full set of 2D drawing methods, to create and styles graphics, text, and images. See the reference for the full listing, and the examples page.

To load p5 onto the page, either setup() or draw() is required (but not both), or you may use instance mode to have more control over when and how your sketch gets loaded.

createCanvas

By default, the drawing canvas has a size of 100x100. If you want to set a custom size, use the createCanvas() function. It is a good idea to always make this the first line in setup(). The code below sets the canvas to size 600x400:

function setup() {
  createCanvas(600, 400);
  line(15, 25, 70, 90);
}

createCanvas() will create a new drawing canvas in the size specified and append it to the html page you are working with. If there is other content in the body of your html page, it will add on to the page after that, so you may not see it at the top of the window.

The canvas will also be affected by any existing CSS (styling) on the page, including default set by your browser. For example, a lot of browsers have a default padding around the end of the page. If you are noticing that your canvas isn't all the way at the upper left corner of your window, this is probably why. To fix this, you can add a line to the <head> of your html file that overrides the default padding, like this:

<style> body{padding:0; margin:0;} </style>

If you would like to specify a location for the canvas, rather than appending directly to the end, you can use the parent() method. In the <body> of your html file, create a container where you would like your canvas to get inserted, with ID of your choice:

<div id='myContainer'></div>

Then use a variable to store a pointer to the canvas you created, and call .parent() on this variable:

function setup() {
  let myCanvas = createCanvas(600, 400);
  myCanvas.parent('myContainer');
}

The p5.js API provides a lot of functionality for creating graphics, but there is some native HTML5 Canvas functionality that is not exposed by p5. You can still call it directly using the variable drawingContext, as in the example below. See this reference for the native canvas API.

function setup() {
  drawingContext.shadowOffsetX = 5;
  drawingContext.shadowOffsetY = -5;
  drawingContext.shadowBlur = 10;
  drawingContext.shadowColor = "black";
  background(200);
  ellipse(width/2, height/2, 50, 50);
}

Mouse and touch interaction

p5.js has a set of methods for handling mouse and touch interaction. The list below shows how they relate conceptually.

mouse touch
touches[]
mouseIsPressed
mousePressed() touchStarted()
mouseMoved()
mouseDragged() touchMoved()
mouseReleased() touchEnded()
mouseClicked()
mouseScrolled()

You may notice with touch, unless you have defined a touchStarted() method, the screen may react with default touch behavior for your mobile device (i.e. your whole screen view may move around when you drag your finger). If you add an empty touchStarted() method, you should see this stop.

Many browsers have default behaviors for various interactions. For example, touchMoved on a mobile device may result in dragging the whole window. To prevent these behaviors, add a return false line at the end of your defined functions.

function touchMoved() {
  // do some stuff
  return false;
}

Asynchronous calls and file loading

JS is single threaded and synchronous, meaning one line of code completes before the next one runs. However, there are asynchronous functions used that make the program flow faster. Loading images, external files, and URLs are generally handled by async functions. (Note: Loading files from the sketch folder will require that you are running a local server, see here for a tutorial about how to set one up on your computer.)

Callbacks

All of the load functions will accept a callback function as an optional last argument. In the following example, an image is drawn once it is loaded.

function setup() {
  createCanvas(400, 240);
  loadImage('cat.jpg', drawCat);
}

function drawCat(img) {
  image(img, 0, 0);
}

Compare this to calling image() directly after loadImage(), which ends up blank:

function setup() {
  createCanvas(400, 240);
  let img = loadImage('cat.jpg');
  image(img, 0, 0);
}

Preload

Alternatively, you could use the preload() function. If a preload() block exists it runs first, then setup() will wait until everything in there has completed before it gets run, so you can make use of things loaded in preload in setup and draw. Note that nothing else should be in preload besides load calls, all other initialization should happen in setup. No callbacks are required for load functions in preload.

let img;
function preload() {
  img = loadImage('cat.jpg');
}

function setup() {
  createCanvas(400, 240);
  image(img, 0, 0);
}

Loading Screen

As mentioned above, if your sketch has large media dependencies such as images, audio or videos, these should be loaded into your sketch using the preload() function. Since loading large files can take some time, you might want to display a loading screen to your users. This will convey that your sketch is indeed running — it just has a lot of cool things to load first. To add a loading screen to your p5.js sketch, all you need to do is include an HTML element on your page with the id p5_loading. It would look something like the following:

<div id="p5_loading" class="loadingclass">this could be some sweet graphics loading lots of bits.</div>

P5.js looks through your HTML to see if you have included an element with the id p5_loading. P5.js will use this element and its content as the loading screen for your sketch. Since the loading element is included in your HTML, you can add any additional classes and/or style the element anyway you see fit.

For an example of a sketch using a loading screen refer to examples/loadingscreen/index.html in the p5.js source.

Browser functions and native JavaScript

There are a few variables and functions that make browser interaction easier, many more to come!

You should also be able to use any native JS functions in your p5.js sketch without a problem, the MDN JavaScript reference is a good place to learn more. Read the next section to learn how to make p5 play extra nice with the rest of your project.

Instantiation / namespace

By default, all p5.js functions are in the global namespace (i.e. bound to the window object), meaning you can call them simply ellipse(), fill(), etc. However, this might be inconvenient if you are mixing with other JS libraries (synchronously or asynchronously) or writing long programs of your own. p5.js currently supports two ways around this problem: "instance mode" and "on-demand global mode". In instance mode, all p5 functions are bound up in a single variable instead of polluting your global namespace.

Consider a program like this:

let x = 100;
let y = 100;

function setup() {
  createCanvas(200,200);
}

function draw() {
  background(0);
  fill(255);
  ellipse(x,y,50,50);
}

A conversion to "instance mode" looks like this:

const s = ( p ) => {

  let x = 100; 
  let y = 100;

  p.setup = function() {
    p.createCanvas(700, 410);
  };

  p.draw = function() {
    p.background(0);
    p.fill(255);
    p.rect(x,y,50,50);
  };
};

let myp5 = new p5(s);

Optionally, you can specify a default container for the canvas and any other elements to append to with a second argument. You can give the ID of an element in your html, or an html node itself:

let myp5 = new p5(s, 'myContainer');

Note that creating instances like this also allows you to have more than one p5 sketch on a single web page, as they will each be wrapped up with their own set up variables. Of course, you could use iframes to have multiple sketches in global mode.

Another option is on-demand global mode. To initialize on-demand global mode at any point in a web page's lifetime, simply call new p5() without any arguments. Calling p5 explicitly this way gives you more control over how your sketch is loaded, which is helpful when you’re working with other libraries.

Along these lines, on-demand global mode also allows p5 to be instantiated before page load, so folks who really want to work around the assigning variables using p5 functions and variables before setup limitation can write the following sorts of sketches:

new p5();

let boop = random(100);

function setup() {
    createCanvas(100, 100);
}

function draw() {
    background(255, 0, boop);
}

Libraries

One of the core ideas behind p5.js is that your sketch is not just the graphics canvas but the whole web page in your browser. For this reason, there is the p5.dom library that makes it easy to interact with other HTML5 objects, including text, hyperlink, image, input, video, audio, and webcam. There is also a p5.sound library that provides a friendly interface to HTML5 web audio API for loading, playing, and synthesizing sounds.

Learn how to use these libraries or develop your own here.

Editor

You can use any editing environment you like, in addition, the p5.js web editor has been officially launched , you can try that out too. The p5.js web editor handles setting up projects and the server is set up internally for you. You can usually run p5.js sketches without a server, but it is required if you are doing certain things like loading external files. Here are the instructions for setting up a local server.

FAQ

How is this different than Processing.js?

The main goal of Processing.js is to execute Processing files in HTML5, but not necessarily to write native HTML5. It supports a mixed syntax of Processing and JavaScript, where the JavaScript is not really meant to be consumed by the end-user. Processing.js is a port of Processing to JS, using regex to convert Java into JS. It is a good tool for those that want to run simple sketches on the web, however, it is quite opaque. It can be difficult for someone to understand how it works, how to fix things when it doesn't work, or how to modify or extend the library. As Processing.js says on their website, "it's not magic, but almost."

In contrast, with p5 we are reimagining Processing's original goals in native JavaScript, in a way that is intended to be transparent and intuitive. It is easy to translate a sketch from Processing to p5 and the process of doing so begins to teach people the basics of JS. From there, they are able to begin writing native JS using a syntax that feels familiar but appropriate for the context. We are closely studying the decisions Processing has made, but also always questioning to see if there are design decisions that would make the library cleaner, stronger, and more intuitive.

Additionally, p5.js extends beyond canvas drawing to allow people to create, access and manipulate other HTML elements. Through this framework beginners begin to explore and understand HTML, JavaScript, and CSS, and the way they work together in the browser. p5.js will also have a system for people to contribute add-on modules to deal with things like audio, video, various input devices, and data. There is also an option to use p5 globally or instantiated within a namespace, so it can be compatible with other JS libraries.

Most importantly, this project is in active development, with enthusiastic support and contributions from around the world. We have begun working with various schools and institutions to teach workshops and classes, in hopes of integrating it into curricula as a tool for understanding the web.

We are also putting a lot of energy into making the documentation clear, for developers as well as users. We'd like this to be a project that anyone feels welcome and empowered to be a part of, whether that's contributing documentation, writing code, teaching with it, or using it to create.

How did this project originate?

This project developed out of a Fellowship with the Processing Foundation exploring the future of Processing with JavaScript. Documentation of research in process and references is here.

Do all the variables have to be in the global namespace? Can I run multiple sketches on one page?

By default, all p5.js functions are in the global namespace (i.e. bound to the window object), meaning you can call them simply ellipse(), fill(), etc. However, this might be inconvenient if you are mixing with other JS libraries or writing long programs of your own. To solve this problem, there is something we call "instance mode", where all p5 functions are bound up in a single variable instead of polluting your global namespace. See more info here.

Why can't I assign variables using p5 functions and variables before setup()?

Well, technically, you can by using on-demand global mode. But that's a less common use of p5, so we'll explain that later and talk about the more common case first. In regular global mode, p5 variable and function names are not available outside setup(), draw(), mousePressed(), etc. (Except in the case where they are placed inside functions that are called by one of these methods.) What this means is that when declaring variables before setup(), you will need to assign them values inside setup() if you wish to use p5 functions. For example:

let n;
function setup() {
  createCanvas(100, 100);
  n = random(100);
}

The explanation for this is a little complicated, but it has to do with the way the library is setup in order to support both global and instance mode. To understand what's happening, let's first look at the order things happen when a page with p5 is loaded (in global mode).

  1. Scripts in <head> are loaded.
  2. <body> of HTML page loads (when this is complete, the onload event fires, which then triggers step 3).
  3. p5 is started, all functions are added to the global namespace.

So the issue is that the scripts are loaded and evaluated before p5 is started, when it's not yet aware of the p5 variables. If we try to call them here, they will cause an error. However, when we use p5 function calls inside setup() and draw() this is ok, because the browser doesn't look inside functions when the scripts are first loaded. This is because the setup() and draw() functions are not called in the user code, they are only defined, so the stuff inside of them isn't run or evaluated yet.

It's not until p5 is started up that the setup() function is actually run (p5 calls it for you), and at this point, the p5 functions exist in the global namespace.

We mentioned on-demand global mode earlier. This mode is most useful when you're building a program that uses other libraries and you want to control how p5 is loaded on the page with the others. You can read more about it here. But another interesting use of on-demand global mode is the ability to call p5 explicitly and then use p5 functions outside of setup(). Here's an example:

new p5();

var boop = random(100);

function setup() {
    createCanvas(100, 100);
}

function draw() {
    background(255, 0, boop);
}

You may get a warning saying that p5.js has been imported twice, but things should still work as expected.