p5.js Beginner Article 1

Functions & Code Organisation

Write your own functions to break sketches into manageable, reusable parts.

⏱ 18 min read functions code organisation parameters return

Why functions matter

As your sketches grow beyond 50 lines, you’ll start noticing problems: draw() becomes a wall of code, the same calculations appear in multiple places, and changing anything feels risky. Functions solve all of this.

A function is a named, reusable block of code. You define it once and call it whenever you need it.

Defining and calling a function

// Definition
function drawStar(x, y) {
  // draws a simple 4-pointed star
  push();
  translate(x, y);
  fill(255, 220, 50);
  noStroke();
  rect(-5, -20, 10, 40);
  rect(-20, -5, 40, 10);
  pop();
}

function draw() {
  background(20);
  // Call it as many times as you like
  drawStar(100, 200);
  drawStar(300, 150);
  drawStar(500, 250);
}

drawStar(x, y) is called with two arguments (100 and 200). Inside the function, these arrive as parameters x and y.

Parameters and default values

You can give parameters default values — the caller can then omit them:

function drawCircleRing(x, y, radius = 80, count = 8, dotSize = 12) {
  for (let i = 0; i < count; i++) {
    let angle = TWO_PI / count * i;
    let px = x + cos(angle) * radius;
    let py = y + sin(angle) * radius;
    circle(px, py, dotSize);
  }
}

function draw() {
  background(20);
  fill(200, 100, 255);
  noStroke();

  drawCircleRing(200, 200);           // uses all defaults
  drawCircleRing(450, 200, 60, 12);   // custom radius and count
}

Return values

Functions can compute something and return it:

function hexToRgb(hex) {
  let r = unhex(hex.slice(1, 3));
  let g = unhex(hex.slice(3, 5));
  let b = unhex(hex.slice(5, 7));
  return color(r, g, b);
}

// Or simpler examples:
function clamp(val, lo, hi) {
  return max(lo, min(hi, val));
}

function randomFrom(arr) {
  return arr[floor(random(arr.length))];
}

Use return to send a value back to the caller:

let palette = ['#ff6432', '#3b82f6', '#22c55e', '#f59e0b'];
let c = randomFrom(palette);
fill(c);

Organising a complex sketch

Here is how you’d break a sketch into logical functions:

// ── State ──────────────────────────────────
let particles = [];
const NUM_PARTICLES = 80;

// ── Lifecycle ──────────────────────────────
function setup() {
  createCanvas(700, 500);
  initParticles();
}

function draw() {
  drawBackground();
  updateParticles();
  drawParticles();
  drawHUD();
}

// ── Initialisation ─────────────────────────
function initParticles() {
  for (let i = 0; i < NUM_PARTICLES; i++) {
    particles.push(createParticle());
  }
}

function createParticle() {
  return {
    x:  random(width),
    y:  random(height),
    vx: random(-1.5, 1.5),
    vy: random(-1.5, 1.5),
    r:  random(4, 14),
    hue: random(180, 280)
  };
}

// ── Update ─────────────────────────────────
function updateParticles() {
  for (let p of particles) {
    p.x += p.vx;
    p.y += p.vy;
    if (p.x < 0 || p.x > width)  p.vx *= -1;
    if (p.y < 0 || p.y > height) p.vy *= -1;
  }
}

// ── Drawing ────────────────────────────────
function drawBackground() {
  background(15, 15, 25, 30);
}

function drawParticles() {
  colorMode(HSB, 360, 100, 100, 100);
  noStroke();
  for (let p of particles) {
    fill(p.hue, 70, 90, 80);
    circle(p.x, p.y, p.r * 2);
  }
}

function drawHUD() {
  colorMode(RGB);
  fill(255);
  noStroke();
  textSize(12);
  text(`${particles.length} particles`, 10, 20);
}

This separation — state, lifecycle, init, update, draw — makes each section easy to reason about independently.

Pure functions

A pure function has no side effects: it only reads its inputs and returns a value. Strive for pure functions wherever possible — they’re easier to test and reuse:

// Pure — returns a new object, doesn't modify anything
function stepParticle(p) {
  return {
    ...p,
    x: p.x + p.vx,
    y: p.y + p.vy
  };
}

// Impure — modifies the particle in place
function stepParticle(p) {
  p.x += p.vx;   // modifies input
  p.y += p.vy;
}

Both approaches work; the pure version is safer in complex sketches.


Key takeaways

  • Functions break your sketch into named, reusable blocks
  • Parameters let you customise behaviour at call time; default values make parameters optional
  • return sends a computed value back to the caller
  • Split your sketch into setup, update, and draw functions — your future self will thank you
  • Aim for small functions that do one thing well