p5.js Starter Article 3

Your First Sketch

Understand setup(), draw(), and the animation loop that makes p5.js tick.

⏱ 15 min read setup draw animation loop

The two essential functions

Every p5.js sketch is built around two functions:

function setup() {
  // Runs once when the sketch starts
}

function draw() {
  // Runs repeatedly — around 60 times per second
}

That’s the whole skeleton. p5.js calls setup() once, then calls draw() over and over until you stop the sketch or close the tab.

Your first sketch — step by step

Open your environment of choice and replace whatever is there with this:

function setup() {
  createCanvas(600, 400);
  background(30);
}

function draw() {
  fill(255, 100, 50);
  noStroke();
  circle(300, 200, 100);
}

Run it. You should see an orange circle on a dark background.

Let’s unpack every line.

createCanvas(600, 400)

This creates the drawing surface — 600 pixels wide, 400 pixels tall. Call this first inside setup(). You can only have one canvas per sketch (unless you’re doing advanced multi-canvas work).

background(30)

Fills the entire canvas with a colour. The number 30 is a grey value on a scale of 0 (black) to 255 (white). Calling background() inside setup() (not draw()) means it only runs once — the canvas gets a dark grey background that persists.

fill(255, 100, 50)

Sets the fill colour for everything drawn after this line. Three numbers = red, green, blue. We’ll cover colour properly in article 6; for now, know that fill(r, g, b) where each value is 0–255.

noStroke()

Removes the outline from shapes. By default, p5.js draws a black outline around everything. noStroke() turns that off.

circle(300, 200, 100)

Draws a circle. The arguments are circle(x, y, diameter). So this circle is centred at x=300, y=200, with a diameter of 100 pixels.

Move background() into draw()

Here is where it gets interesting. Move the background call inside draw():

function setup() {
  createCanvas(600, 400);
}

function draw() {
  background(30);             // ← moved here
  fill(255, 100, 50);
  noStroke();
  circle(mouseX, mouseY, 100);  // ← follows the mouse
}

The circle now follows your mouse. The background() at the top of draw() clears the canvas before drawing the circle again each frame — so you only ever see one circle.

What happens without background()?

Remove it:

function setup() {
  createCanvas(600, 400);
}

function draw() {
  fill(255, 100, 50, 40);  // 40 = slightly transparent
  noStroke();
  circle(mouseX, mouseY, 100);
}

Now every frame adds a new circle on top of the last. Move the mouse slowly and you get a paint trail. This technique — omitting or fading the background — is one of the simplest tricks for creating accumulative visuals.

frameRate() and frameCount

p5.js targets 60 frames per second by default. Two built-in variables are useful:

  • frameCount — how many frames have been drawn since the sketch started (starts at 1)
  • frameRate() — call with no arguments to read the current FPS
function setup() {
  createCanvas(600, 400);
}

function draw() {
  background(30);
  fill(255);
  textSize(16);
  text('Frame: ' + frameCount, 20, 30);
  text('FPS: ' + floor(frameRate()), 20, 55);
}

You can also set the frame rate: frameRate(30) runs at 30fps. This matters for performance and for animations where you deliberately want a slower, choppier look.

noLoop() and loop()

Calling noLoop() inside setup() makes draw() run exactly once. Useful for static generative images:

function setup() {
  createCanvas(600, 600);
  noLoop();
}

function draw() {
  background(20);
  // Everything here runs once
  for (let i = 0; i < 200; i++) {
    fill(random(255), random(255), random(255), 150);
    noStroke();
    circle(random(width), random(height), random(5, 40));
  }
}

Call loop() from anywhere (e.g., from mousePressed()) to restart the loop.

The global variables: width and height

After createCanvas() runs, width and height hold the canvas dimensions. Use them instead of hard-coded numbers:

function setup() {
  createCanvas(600, 400);
}

function draw() {
  background(30);
  // Centre of the canvas, regardless of size
  circle(width / 2, height / 2, 100);
}

Key takeaways

  • setup() runs once; draw() loops ~60 times per second
  • createCanvas(w, h) defines your drawing surface
  • Call background() at the top of draw() to clear each frame — or omit it to accumulate
  • width and height always reflect your canvas size
  • noLoop() makes the sketch run once; loop() restarts it