Sketch structure

A p5.js sketch is typically structured around four primary components:

  • setup() – Runs once at the beginning to initialize the canvas and set up variables.
  • draw() – Runs continuously in a loop to update and render visuals.
  • Interactive functions – Respond to user input like mouse, keyboard, or touch events.
  • Custom functions – Help organize and reuse code.

Here’s a basic sketch structure in p5.js:

function setup() {
  // Set up the canvas and initialize variables
}

function draw() {
  // Main animation and rendering loop
}

// Interactive functions (mouse, keyboard, touch)

function mouseClicked() {
  // Read mouseX and mouseY to capture click position
  // Respond to the click
}

// Custom functions

function drawCircle(x, y, size, color, x, y) {
  // Draw a circle with the given parameters
}

Setup

The setup() function runs once at the start of the program. It is used to:

  • Define the canvas size with createCanvas(width, height).
  • Initialize variables and objects for the sketch.

Example:

function setup() {
  createCanvas(400, 400);
  background(220); // Light gray background
}

This creates a 400x400 canvas with a light gray background.

ℹ️
To load external assets like images, fonts, or sounds, define setup() as async and use await loadImage(...), await loadFont(...), etc.

Asset Loading Example

Here’s an example that loads an image and uses it as the background:

Code Explanation
let pola;

async function setup() {
  createCanvas(300, 300);
  pola = await loadImage('pola.jpg');
  background(pola);
}

Draw

The draw() function is the core of animation and interaction in p5.js. Unlike setup(), which runs once, draw() runs continuously, executing frame by frame. By default, this happens 60 times per second unless modified with frameRate().

Continuous Drawing

By default, visuals in draw() accumulate, meaning previous frames are not erased. This creates a layering effect, where each frame builds on the last. The example below shows this behavior, where a red circle follows the mouse, leaving a trailing effect.

Code Explanation
function setup() {  
  createCanvas(300, 300);  
}  

function draw() {  
  fill(0, 255, 0);  
  circle(100, 200, 80);  
  fill(0, 0, 255);  
  circle(200, 200, 80);  
  fill(255, 0, 0, 25);  
  circle(mouseX, mouseY, 80);  
}  
ℹ️
  • draw: Runs continuously, enabling animation and interaction.
  • mouseX / mouseY: Track the mouse position on the canvas.

Smooth Refresh

To avoid visuals stacking up, the background() function clears the canvas at the start of each frame, ensuring only the latest frame is displayed.

Code Explanation
function setup() {  
  createCanvas(300, 300);  
}  

function draw() {  
  background(70);  
  fill(0, 255, 0);  
  circle(100, 200, 80);  
  fill(0, 0, 255);  
  circle(200, 200, 80);  
  fill(255, 0, 0, 125);  
  circle(mouseX, mouseY, 80);  
}  
ℹ️
  • background: Clears the canvas each frame to prevent overlapping visuals.
  • Without background(), frames stack, leaving a trail effect.
  • With background(), only the current frame is visible, creating smooth motion.

Interactive Functions

Interactive functions like mouseClicked(), mouseMoved() for the mouse, and keyPressed() for the keyboard run automatically when the corresponding event occurs, making interactivity easy to implement.

Mouse and Keyboard Example

(Click anywhere to reposition the red circle; press any key to reset it to the center)

Code Explanation
let pola;
let redX, redY;

async function setup() {
  createCanvas(300, 300);
  pola = await loadImage('pola.jpg');
  redX = width / 2;
  redY = height / 2;
}

function draw() {
  background(pola);
  fill(0, 255, 0);  
  circle(100, 200, 80);  
  fill(0, 0, 255);  
  circle(200, 200, 80);  
  fill(255, 0, 0, 125);  
  circle(redX, redY, 80);
}

function mouseClicked() {
  redX = mouseX;
  redY = mouseY;
}

function keyPressed() {
  redX = width / 2;
  redY = height / 2;
}

Custom Functions

To keep code organized, you can define custom functions:

function drawCircle(size, color, x = width / 2, y = height / 2) {
  push();
  fill(color);
  circle(x, y, size);
  pop();
}

and then call it in draw (or whenever it is needed):

function draw() {
  background(pola);
  drawCircle(80, color(0, 255, 0), 100, 200);
  drawCircle(80, color(0, 0, 255), 200, 200);
  drawCircle(80, color(255, 0, 0, 125), mouseX, mouseY);
}

This helps structure the sketch better, making it modular and reusable.

Further Reading

📌 p5.js Reference: Sketch Structure – Learn more about setup(), draw(), and asset loading with await.