Creative Programming Reference

Creative Programming 2026

This is the official coding reference for Creative Programming 2026.

As the course progresses, this page will be updated with all the key concepts, functions, and techniques covered in lectures. Use this as your go-to resource for looking up how to do things in p5.js, understanding programming concepts, and finding examples to inspire your projects.

That said, there may be inaccuracies and missing information, so always refer back to the official p5.js documentation for the most up-to-date and comprehensive information: https://p5js.org/reference/.

Finally, this page does not include interactive coding examples. Please go over the lecture slides (or the respective coding folder) to see the code in action and experiment with it yourself. The best way to learn programming is by doing, so don’t just read this reference—try out the code and make it your own!

1 Getting Started with p5.js

Lecture 1

p5.js is a JavaScript library that makes creative coding accessible. It allows you to create interactive graphics and animations directly in your browser. Every p5.js sketch has two essential functions that form the foundation of your code:

function setup() {
  // Runs once at the start of the sketch
}

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

1.1 Key Rules

  • All statements end with a semicolon ;
  • Comments use // for single lines or /* */ for multiple lines
  • Strings are enclosed in quotes: "text" or 'text'
  • Functions require parentheses: functionName()
  • Arguments inside parentheses are separated by commas

1.2 Coordinate System

  • Top-left corner is (0, 0)
  • X-axis goes horizontally left to right
  • Y-axis goes vertically top to bottom

2 Canvas & Drawing Setup

Lecture 1

Before you can draw anything, you need to create a canvas (the area where your graphics appear) and set up the visual properties like colours and line thickness. The canvas acts as your blank page, and you control everything that appears on it.

2.1 Creating a Canvas

In the setup() function, you must create a canvas using createCanvas(). This function takes two arguments: the width and height of your canvas in pixels. The canvas is created only once, in setup().

function setup() {
  createCanvas(800, 600);  // width, height in pixels
}

If you don’t create a canvas, p5.js will create a default 100×100 pixel canvas for you. You can resize the canvas at any time by calling createCanvas() again.

2.2 Setting Colours

Use fill() to set the colour of shapes that you draw after it, and background() to set the background colour of your canvas. These are called “state-setting” functions—once you set a colour, all subsequent shapes use that colour until you change it.

background(255);           // White background
fill(255, 0, 0);          // Red fill

circle(100, 100, 50);     // This circle is red
fill(0, 0, 255);           // Change fill to blue
circle(200, 200, 50);     // This circle is blue

Typically, you call background() at the beginning of draw() to clear the canvas and start fresh each frame. This creates the illusion of animation as shapes move around the screen.

2.3 Drawing Basic Shapes

The coordinate system for all shapes places the origin (0, 0) at the top-left corner of the canvas. All positions are measured in pixels from there.

Function Purpose Example
point(x, y) Draw a single pixel point(100, 50)
circle(x, y, size) Draw a circle at (x, y) with given diameter circle(200, 200, 50)
ellipse(x, y, width, height) Draw an ellipse (oval) ellipse(200, 200, 100, 50)
rect(x, y, width, height) Draw a rectangle from top-left rect(100, 100, 200, 150)
text(string, x, y) Display text on canvas text("Hello", 50, 100)
textSize(size) Set text size before drawing text textSize(24)
strokeWeight(thickness) Set thickness of points and shape outlines strokeWeight(3)

2.4 Visual Layering

Shapes drawn later appear on top of earlier ones. The order of your drawing commands matters!


3 Random Numbers

Lecture 1

Randomness is key to interesting creative work. Using the random() function, you can generate unexpected variations that keep your sketches feeling fresh and alive rather than mechanical and repetitive.

Generate random values for variation:

random()           // Random float between 0 and 1
random(5)          // Random float between 0 and 5
random(10, 50)     // Random float between 10 and 50
int(random(1, 7))  // Random integer from 1 to 6 (dice roll)

3.1 Example: Random Circles

function draw() {
  fill(random(255), random(255), random(255));
  circle(random(width), random(height), random(20, 50));
}

4 System Variables

Lectures 1-2

p5.js automatically creates and updates these variables for you. You can read their values at any time to make your sketch respond to user input, time, and motion. These are often called “system variables” or “magic variables” because they’re maintained invisibly by p5.js.

Variable Description
mouseX Horizontal position of the mouse
mouseY Vertical position of the mouse
pmouseX Previous horizontal mouse position (frame before)
pmouseY Previous vertical mouse position (frame before)
width Horizontal size of the canvas
height Vertical size of the canvas
frameCount Number of times draw() has been called since start

4.1 Example: Following Mouse

function draw() {
  background(255);
  circle(mouseX, mouseY, 50);  // Follows mouse
}

4.2 Example: Frame-Based Animation

function draw() {
  background(255);
  let x = 100 + sin(frameCount * 0.05) * 50; // we use sin to create a smooth oscillation
  circle(x, 200, 30);  // Oscillates back and forth
}

5 Variables & Data Types

Lecture 2

Variables are the foundation of interactive and dynamic programs. They allow you to store information, change it over time, and reuse values throughout your code. Every piece of data in programming has a type, and p5.js supports several important types you’ll use frequently.

5.1 What is a Variable?

A variable is a named container that stores data. Think of it as a labeled box where you can put a value inside. You declare variables with the let keyword, giving your variable a name and an initial value:

let variableName = value;

Once a variable is created, you can use its name anywhere in your code to access its current value. You can also change what’s inside by reassigning it (without using let again):

variableName = newValue;  // Change the value

Variables are especially powerful when combined with loops and conditions. For example, you might store a ball’s position in a variable, and update that position each frame to create animation.

5.2 Variable Scope

Scope is where a variable is available. Variables declared inside a function or loop only exist there. Variables declared outside functions are global and can be used anywhere.

let globalX = 100; // Global scope

function setup() {
  let localY = 50; // Local to setup()
}

function draw() {
  background(255);
  circle(globalX, 100, 30);
  // circle(localY, 100, 30); // This would cause an error!
}

Tip: define shared variables above setup() if you need them in multiple functions.

5.3 Data Types

Data types define what kind of information a variable can hold. Here are the most common data types you’ll encounter in p5.js:

5.3.1 String

Text enclosed in quotes:

let greeting = "Hello, world!";
let name = 'Alice';

5.3.2 Integer

Whole numbers (positive, negative, or zero):

let age = 42;
let negative = -10;
let zero = 0;
let converted = int("123");  // Convert from string

5.3.3 Float

Numbers with decimal points:

let pi = 3.14;
let temperature = -2.5;
let rounded = round(3.7);   // Returns 4
let floored = floor(3.7);   // Returns 3
let ceiled = ceil(3.2);     // Returns 4

5.3.4 Boolean

Logical values true or false:

let isRaining = true;
let isSunny = false;

5.3.5 Colour

Colours can be stored and reused:

let red = color(255, 0, 0);
let transparent = color(255, 0, 0, 127);  // RGBA
let fromHex = color('#FF0000');
let named = color('red');

5.4 Shorthand Operators

Quickly update number variables:

x += 5;      // x = x + 5
x -= 3;      // x = x - 3
x *= 2;      // x = x * 2
x /= 4;      // x = x / 4
x++;         // x = x + 1
x--;         // x = x - 1

5.5 Concatenate Strings

Combine strings together:

let title = "King";
let firstName = "Frederik";
let fullName = title + " " + firstName;  // "King Frederik"
let greeting = "Hello, " + fullName + "!";  // "Hello, King Frederik!"

6 Logic & Control Flow

Lecture 2

Logic allows your program to make decisions and respond to different situations. By using conditions and if statements, you can create interactive programs that behave differently depending on the input or state of your sketch. This is what makes programs come alive—they can react and adapt.

6.1 Comparison Operators

Comparison operators evaluate two values and return either true or false. These are the building blocks of all conditional logic. Every condition you write uses one of these operators to compare values:

x == 5          // Equal to: exactly 5
x != 5          // Not equal to: anything except 5
x < 5           // Less than: 4, 3, 2, 1, 0, -1, etc.
x > 5           // Greater than: 6, 7, 8, etc.
x <= 5          // Less than or equal to: 5, 4, 3, etc.
x >= 5          // Greater than or equal to: 5, 6, 7, etc.

These comparisons work with any data types but are most common with numbers. You can also compare strings:

name == "Alice"        // String comparison
age >= 18              // Number comparison

Important: Be careful not to confuse == (comparison) with = (assignment). A very common mistake is writing if (x = 5) when you meant if (x == 5). The first one assigns 5 to x, while the second one checks if x equals 5.

6.2 If Statement

An if statement checks whether a condition is true. If it is, the code inside the curly brackets { } runs. If the condition is false, that code is skipped.

if (condition) {
  // This code only runs if condition is true
}

Example:

if (mouseX < 200) {
  fill(255, 0, 0);  // Red - only runs if mouse is on left side
  circle(100, 100, 50);
}
// If mouseX >= 200, the circle is never drawn

If statements are fundamental to making interactive programs. They let your sketch respond to user input, time, or other changing values in your program. You can also nest if statements (put one inside another) for more complex logic.

6.3 If-Else Statement

An if-else statement provides two paths: one if the condition is true, and a different one if it’s false. This is useful when you want your program to do one thing in some situations and something different in others.

if (condition) {
  // Code runs if condition is true
} else {
  // Code runs if condition is false
}

Example:

if (mouseX < 200) {
  fill(255, 0, 0);  // Red fill
  circle(mouseX, mouseY, 50);
} else {
  fill(0, 0, 255);  // Blue fill
  circle(mouseX, mouseY, 50);
}
// The circle is always drawn, but its colour depends on mouse position

You can also chain multiple conditions together with else if for more complex decision trees:

if (mouseX < 200) {
  fill(255, 0, 0);  // Red
} else if (mouseX < 400) {
  fill(0, 255, 0);  // Green
} else {
  fill(0, 0, 255);  // Blue
}

6.4 Logical Operators

Logical operators let you combine multiple conditions. This is powerful when you need your code to react to multiple factors at once.

condition1 && condition2    // AND: both conditions must be true
condition1 || condition2    // OR: at least one condition must be true
!condition                  // NOT: reverses true to false, false to true

Examples:

// AND example: both must be true
if (x > 100 && x < 300) {
  // Code runs only if x is between 100 and 300
}

// OR example: at least one must be true
if (key == 'a' || key == 'b') {
  // Code runs if the user pressed either 'a' or 'b'
}

// NOT example: reverses the condition
if (!isRaining) {
  // Code runs if it's NOT raining
}

// Complex example: combining operators
if ((mouseX > 100 && mouseX < 300) || mouseY < 50) {
  // Code runs if x is in range OR y is near the top
}

The AND operator (&&) is useful for checking ranges, while OR (||) is useful for checking multiple discrete options. The NOT operator (!) is useful for checking if something is false.


7 Loops & Iteration

Lecture 2

Loops allow you to repeat code multiple times without writing it out over and over. They’re essential for creating patterns, drawing grids, animating multiple objects, and processing arrays. A small loop can accomplish what would otherwise require hundreds of lines of code.

7.1 For Loop

A for loop repeats code a specific number of times. It’s perfect for drawing multiple shapes, creating patterns, or processing every element in an array.

for (let i = 0; i < 5; i++) {
  // This code runs 5 times
  // First time: i = 0
  // Second time: i = 1
  // ... continues until i = 4
  circle(i * 50, 100, 20);  // Draw circles in a row
}

7.1.1 For Loop Structure

A for loop has three parts separated by semicolons:

  1. Initialization (let i = 0): Set up a counter variable that tracks which iteration you’re on
  2. Condition (i < 5): A condition that’s checked before each iteration. The loop stops when this becomes false
  3. Update (i++): How to change the counter after each iteration (usually increment by 1)

Think of it like counting: “Start at 0, keep going while less than 5, add 1 each time.”

You can also use other update patterns:

for (let i = 10; i > 0; i--) {
  // Counts down from 10 to 1
}

for (let i = 0; i < 100; i += 10) {
  // Counts up by 10s: 0, 10, 20, 30, ...
}

7.1.2 Nested Loops

Loops inside loops create grids and 2D patterns. When you have nested loops, the inner loop completes all its iterations for each iteration of the outer loop:

for (let row = 0; row < 3; row++) {
  for (let col = 0; col < 3; col++) {
    // This creates a 3×3 grid (9 total iterations)
    // First row: col 0, 1, 2
    // Second row: col 0, 1, 2
    // Third row: col 0, 1, 2
    circle(col * 100 + 50, row * 100 + 50, 30);
  }
}

This is extremely useful for creating chessboards, pixel grids, and other 2D arrangements. The key is that the inner loop’s counter can be combined with the outer loop’s counter to calculate unique positions.

7.2 Loop Scope

Variables defined inside loops are only accessible within that loop. Once the loop ends, the variable ceases to exist:

for (let i = 0; i < 5; i++) {
  let temp = i * 2;  // temp only exists inside the loop
}
// console.log(temp);  // This would cause an error!

This is actually a good thing—it keeps your code organised and prevents accidental misuse of variables.

8 Arrays

Lecture 2

Arrays are collections of related values stored in a single variable. Instead of creating separate variables for each value, arrays let you organise data efficiently and process it with loops. Arrays are essential for managing multiple objects, storing sequences of data, and building more complex programs.

8.1 What is an Array?

An array is a collection of values stored in a single variable:

let colours = ['red', 'green', 'blue'];
let numbers = [10, 20, 30, 40];

8.2 Accessing Elements

Each value in an array has a position called an index. Importantly, arrays are zero-indexed, meaning the first element is at index 0, the second is at index 1, and so on. This is a common source of confusion for beginners, so take time to practice it:

let colours = ['red', 'green', 'blue'];
let first = colours[0];      // 'red' (position 0)
let second = colours[1];     // 'green' (position 1)
let third = colours[2];      // 'blue' (position 2)
let fourth = colours[3];     // undefined (doesn't exist!)

You can also access elements using negative indices (counting from the end) in some cases, but it’s less common. Better to stick with forward indexing when learning.

8.3 Array Properties and Methods

Arrays have built-in properties and methods that let you interact with them:

let colours = ['red', 'green', 'blue'];

colours.length              // 3 (number of elements in the array)
colours.push('yellow');     // Add 'yellow' to the end → array now has 4 elements
colours.pop();              // Remove the last element (yellow)
colours.sort();             // Sort alphabetically (blue, green, red)
colours.reverse();          // Reverse the order

These methods are very useful for managing collections of data. push() and pop() are especially common when you want to add or remove items dynamically.

8.4 Using Arrays with Loops

This is where arrays truly shine—you can iterate through all array elements using a loop:

let colours = [255, 200, 150, 100];
for (let i = 0; i < colours.length; i++) {
  // Each iteration, i goes from 0, 1, 2, 3
  // colours.length is 4, so the loop runs 4 times
  fill(colours[i]);
  rect(i * 50, 100, 50, 100);
}

Notice how we use colours.length in the condition. This is much better than writing i < 4 directly, because if you change the array, the loop still works correctly.

8.5 Multi-dimensional Arrays

Arrays can contain other arrays for grid-like data:

let grid = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

let value = grid[1][2];  // Access row 1, column 2 → 6

As there are two levels of indexing, the first index selects the row, and the second index selects the column within that row.


9 Colours & Colour Systems

Lecture 3

Colour is how you bring your creative visions to life. Understanding how computers represent colour allows you to create palettes, generate gradients, and in general just make things that are very pretty. p5.js gives you multiple ways to specify colours, each useful in different situations.

9.1 Colour Models

p5.js supports multiple ways to specify colours, giving you flexibility depending on your needs.

9.1.1 Grayscale (0-255)

The simplest colour system uses a single value from 0 (black) to 255 (white). Everything in between is gray:

let white = color(255);    // Bright white
let gray = color(128);     // Medium gray
let black = color(0);      // Black
let darkGray = color(50);  // Very dark

Higher values = lighter; lower values = darker. This is useful for simple monochrome sketches or when you want quick variation.

9.1.2 RGB (Red, Green, Blue)

This is the primary colour model used by screens. Every colour is made by mixing red, green, and blue light in different amounts (0-255 for each):

let red = color(255, 0, 0);      // Full red, no green, no blue
let green = color(0, 255, 0);    // Full green
let blue = color(0, 0, 255);     // Full blue
let yellow = color(255, 255, 0); // Red + green = yellow
let cyan = color(0, 255, 255);   // Green + blue = cyan
let magenta = color(255, 0, 255); // Red + blue = magenta
let purple = color(134, 42, 200); // Custom mix
let white = color(255, 255, 255); // All channels full
let black = color(0, 0, 0);       // All channels off

RGB is the most common colour model in digital art and design. You can explore and learn colours by experimenting with the three channels.

9.1.3 Hexadecimal

Hexadecimal colours use a compact notation often seen in web design and design software. Each pair of characters represents one colour channel in the format #RRGGBB:

let red = color('#FF0000');      // #RRGGBB format
let green = color('#00FF00');
let blue = color('#0000FF');
let yellow = color('#FFFF00');
let purple = color('#862ac8');
let lightBlue = color('#ADD8E6');

You can find hex colour codes in Photoshop, design tools like Figma, or online colour pickers. Copy and paste them directly into your code.

9.1.4 Colour Names

For common colours, p5.js recognises CSS colour names:

let red = color('red');
let green = color('green');
let navy = color('navy');
let coral = color('coral');
let teal = color('teal');
let gold = color('gold');

This is convenient for readable code, though it’s limited to predefined colours. You can look up a full list of CSS colour names online.

9.1.5 RGB with Alpha (Transparency)

Add a fourth value for transparency. Alpha ranges from 0 (fully transparent/invisible) to 255 (fully opaque/solid):

let opaque = color(255, 0, 0, 255);        // Fully opaque
let semiTransparent = color(255, 0, 0, 127);  // 50% transparent
let veryTransparent = color(255, 0, 0, 50);   // 80% transparent

Alpha ranges from 0 (fully transparent) to 255 (fully opaque). Transparency is powerful for creating layered effects, trails, and blending shapes together:

let opaque = color(255, 0, 0, 255);         // Fully opaque red
let semiTransparent = color(255, 0, 0, 127); // 50% transparent red
let veryTransparent = color(255, 0, 0, 50);  // 80% transparent red

9.2 Storing and Using Colours

Instead of hardcoding colours everywhere, store them in variables. This makes your code more maintainable and lets you change the colour scheme easily:

let primaryColour = color(255, 100, 0);
let secondaryColour = color(0, 100, 255);
let backgroundColour = color(245, 245, 245);

function draw() {
  background(backgroundColour);
  
  fill(primaryColour);
  circle(100, 100, 50);
  
  fill(secondaryColour);
  circle(200, 200, 50);
}

If you decide to change your colour scheme, you only need to update the three colour variables at the top. This is much easier than finding and changing colours throughout your entire sketch.

9.3 Colour Arrays for Palettes

You can create a palette by storing multiple colours in an array. This is perfect for creating patterns or cycling through colours:

let palette = [
  color(255, 0, 0),      // Red
  color(0, 255, 0),      // Green
  color(0, 0, 255),      // Blue
  color(255, 255, 0),    // Yellow
  color(255, 0, 255)     // Magenta
];

for (let i = 0; i < palette.length; i++) {
  fill(palette[i]);
  rect(i * 100, 50, 100, 100);

}

10 Images & Transformations

Lecture 3

Images and transformations let you move beyond simple shapes to create more sophisticated visuals. Transformations—like moving, rotating, and scaling—let you manipulate everything on the canvas, while image loading lets you incorporate photography and pre-made graphics into your sketches. Combined with loops, these tools unlock enormous creative possibilities.

10.1 Loading Images

Images must be loaded in the preload() function, which runs before setup(). This ensures images are ready before your sketch tries to use them. The preload() function is specifically designed for loading files:

let catImage;
let backgroundImage;

function preload() {
  // Load local files (must be in the same folder as your sketch)
  catImage = loadImage('cat.png');
  backgroundImage = loadImage('background.jpg');
  
  // Or load from the web using a full URL
  // catImage = loadImage('https://example.com/image.png');
}

function draw() {
  // Draw the image at position (50, 50), width 200, height 150
  image(catImage, 50, 50, 200, 150);
  
  // Or draw without specifying size (uses original dimensions)
  // image(backgroundImage, 0, 0);
}

The image() function takes the image object, its x position, y position, and optionally its width and height. If you don’t specify size, the image displays at its original dimensions, which might be too large or too small for your canvas.

10.2 Tinting Images

Apply colour and transparency to images using tint(). This is useful for creating colour overlays, fading effects, or matching images to your colour scheme:

function draw() {
  // Tint the image red with 50% transparency
  tint(255, 0, 0, 127);
  image(catImage, 50, 50, 200, 150);
  
  // Tint with blue and full opacity
  // tint(0, 0, 255, 255);
  // image(catImage, 300, 50, 200, 150);
}

Without tint(), images display in their original colours. Once you call tint(), it affects all subsequent images until you call noTint().

10.3 Translation

translate() moves the origin point (affects all subsequent drawing). This is useful for positioning groups of shapes or creating complex compositions:

translate(200, 100);      // Move origin to (200, 100)
circle(0, 0, 50);         // Draws at (200, 100), not (0, 0)
rect(-25, -25, 50, 50);   // Rectangle also positioned relative to new origin

Important: Translation resets at the end of each draw() cycle, so you need to call translate() again each frame if you want the effect to persist. Also note that mouse coordinates (mouseX, mouseY) are still relative to the original coordinate system, not the translated one. This can be confusing—keep track of your coordinate systems!

10.4 Rotation

rotate() rotates shapes around the current origin. By default, angles are measured in radians (2π = 360°), but you can use angleMode(DEGREES) for more intuitive degree-based rotation:

angleMode(DEGREES);       // Use degrees instead of radians (optional but recommended)
rotate(45);               // Rotate 45 degrees
rect(-25, -25, 50, 50);   // Rectangle rotates around origin

// Or using radians (default)
// rotate(PI / 4);  // Same as 45 degrees

Default unit is radians where 2π ≈ 6.28 = 360°. Use angleMode(DEGREES) at the start of setup() for intuitive degree values. Rotation is particularly powerful when combined with frameCount for animations.

10.5 Scaling

scale() changes the size of shapes and images:

scale(2);                 // Double size of everything drawn after this
circle(0, 0, 50);        // Appears as diameter 100 (2x larger)

scale(1, 0.5);            // Scale x by 1 (no change), y by 0.5 (half)
rect(0, 0, 100, 100);    // Stretched horizontally, squished vertically

You can use different scale factors for x and y to create interesting distortions. Negative scale values create mirror effects.

10.6 Push and Pop

push() and pop() are essential for managing transformations and other changes to the state (e.g. fill()). push() saves the current state, and pop() restores it. This prevents transformations from affecting unintended shapes:

function draw() {
  background(255);
  
  push();                   // Save state
  translate(200, 100);
  rotate(45);
  fill(255, 0, 0);
  circle(0, 0, 50);
  pop();                    // Restore state (no more translation/rotation)

  fill(0, 0, 255);
  circle(50, 50, 50);       // Not affected by translate/rotate above
}

Think of push() and pop() like saving a checkpoint in a video game. After pop(), you’re back to exactly where you were before push(). This is crucial when you have complex transformations—without it, all subsequent drawing would be affected.

10.7 Animation with Transformations

Create spinning effects using frameCount:

function draw() {
  background(255);  // Clear canvas each frame
  
  translate(width / 2, height / 2);  // Center origin
  rotate(frameCount * 2);            // Rotate based on frame count
  rect(-25, -25, 50, 50);
}

The frameCount variable increments every frame, so you get continuous rotation. Multiply by a factor to control speed: frameCount * 2 spins twice as fast as frameCount * 0.5.

10.8 Mouse Trails

Create visual trails by not clearing the background. This keeps previous frames on screen:

function draw() {
  // Don't call background() - previous frames stay visible
  fill(255, 0, 0, 50);    // Semi-transparent red
  circle(mouseX, mouseY, 20);
  
  // Shapes drawn this frame appear on top of previous frames
}

This technique creates beautiful trails as the mouse moves. The transparency (alpha value) controls how quickly the trail fades. Lower alpha = faster fade; higher alpha = longer persistence. Combine this with symmetry for even more striking effects.

10.9 Symmetry Patterns

Combine loops with rotation to create radially symmetrical designs. This is powerful for creating mandalas, spirals, and other geometric patterns:

function draw() {
  background(255);
  translate(width / 2, height / 2);  // Center canvas
  
  for (let i = 0; i < 12; i++) {
    rotate(360 / 12);      // Rotate 30 degrees each iteration

    fill(i * 20);
    rect(0, 50, 30, 30);
  }
}

11 Functions

Lecture 4

Functions are reusable blocks of code that encapsulate specific tasks. They help you avoid repetition, make sketches easier to understand, and let you organise complex code into manageable pieces. Once you write a function, you can call it many times with different inputs, eliminating code duplication.

11.1 Why Use Functions?

Without functions, drawing the same shape in 10 locations means repeating code 10 times. With functions, write it once and reuse it. Functions make code:

  • Shorter and easier to read
  • Easier to maintain – change one place instead of many
  • Reusable – use across projects
  • Better organised – complex sketches broken into logical pieces

11.2 Function Anatomy

A function has a name and optional parameters (inputs):

function functionName(a, b, c) {
  // code to be executed
  // can use a, b, c as variables here
}

The values in parentheses (a, b, c) are parameters – placeholders for the values you’ll pass in. They act as variables that only exist inside the function. You can have as many parameters as you need, or none at all.

11.3 Calling Functions

To run a function, call it by name and pass values:

functionName(1, 2, 3);  // Pass values for a, b, c
functionName(10, 20, 30);  // Call again with different values

Each value you pass is an argument, and arguments fill parameters in order.

11.4 Returning Values

Functions can calculate and return a result:

function add(a, b) {
  return a + b;  // Send result back
}

let sum = add(5, 10);
console.log(sum); // Output: 15

let result = add(100, 200);
console.log(result); // Output: 300

When return executes, the function stops and sends the value back.

11.5 Custom Shape Functions

A powerful use case is drawing repeated shapes without duplicating code:

function flower(x, y, petals) {
  push();  // Save current state
  translate(x, y);  // Move to (x, y)
  for (let i = 0; i < petals; i++) {
    rotate(360 / petals);
    ellipse(0, 40, 20, 60);
  }
  pop();  // Restore state
}

function draw() {
  background(255);
  flower(200, 200, 10);  // 10 petals
  flower(400, 300, 6);   // 6 petals
  flower(100, 150, 12);  // 12 petals
}

11.6 Function Scope

Variables defined inside a function only exist inside that function:

function calculateArea(width, height) {
  let area = width * height;
  return area;
}

function draw() {
  let result = calculateArea(10, 20);
  console.log(result);  // 200 – works
  console.log(area);    // Error! doesn't exist here
}

Variables defined outside all functions are global:

let globalCounter = 0;  // Available everywhere

function increment() {
  globalCounter = globalCounter + 1;
}

function draw() {
  increment();
  console.log(globalCounter);  // Works here too
}

12 Custom Shapes with Vertices

Lecture 4

p5.js provides built-in functions for basic shapes like circles and rectangles. But what for pentagons, stars, or custom designs? That’s where custom shapes come in.

Using beginShape(), vertex(), and endShape(), you can create any polygon or shape you imagine. Always follow the following procedure:

  1. beginShape() – Signal the start of a custom shape
  2. vertex(x, y) – Add a corner point (call multiple times)
  3. endShape(CLOSE) – End the shape. CLOSE connects last vertex back to first

12.1 Basic Example: Triangle

function draw() {
  background(255);
  
  beginShape();
  vertex(100, 50);    // Top
  vertex(200, 200);   // Bottom-right
  vertex(0, 200);     // Bottom-left
  endShape(CLOSE);    // Connects back to first
}

Three vertices define three corners. endShape(CLOSE) connects them into a closed triangle.

12.2 A Complete Example: Diamond Shape

function drawDiamond(x, y, size) {
  push();  // Save state
  translate(x, y);  // Move origin to (x, y)
  
  fill(255, 100, 150);
  beginShape();
  vertex(0, -size);   // Top
  vertex(size, 0);    // Right
  vertex(0, size);    // Bottom
  vertex(-size, 0);   // Left
  endShape(CLOSE);
  
  pop();  // Restore state
}

function draw() {
  background(255);
  drawDiamond(200, 200, 50);
  drawDiamond(400, 300, 40);
  drawDiamond(100, 100, 35);
}

Using push() and pop() protects your drawing state – transformations only affect the current shape.


13 Mouse Events

Lecture 4

While mouseX and mouseY give you the current position every frame, p5.js also provides event functions that automatically run when specific mouse interactions happen. These let you respond to clicks, drags, and scrolling in powerful ways.

13.1 Common Mouse Events

Event When It Fires Best For
mousePressed() When button is first pressed Starting actions
mouseReleased() When button is released Ending actions
mouseClicked() When button is pressed and released Simple clicks
mouseMoved() Every time mouse moves Tracking movement
mouseDragged() When mouse moves while held Drawing, dragging
mouseWheel(event) When wheel scrolls Zooming, changing values

13.2 Check whether mouse button is down

Use mouseIsPressed to check if any mouse button is currently held down. mouseButton tells you which button is pressed (LEFT, RIGHT, CENTER):

function draw() {
  if (mouseIsPressed) {
    console.log("Mouse button is currently down: ", mouseButton);
  }
}

13.3 Pressed – Button Down

Fires once when the mouse button goes down:

let clicks = 0;

function mousePressed() {
  clicks = clicks + 1;
  console.log("Clicks: " + clicks);
}

function draw() {
  background(255);
  textSize(32);
  text("Clicks: " + clicks, 50, 50);
}

Every click increments the counter.

13.4 Released – Button Up

Fires when the mouse button is released:

let isDragging = false;

function mousePressed() {
  isDragging = true;
}

function mouseReleased() {
  isDragging = false;
  console.log("Drag ended");
}

function draw() {
  background(255);
  textSize(24);
  text(isDragging, 50, 50);
}

13.5 Drawing with Dragged

Dragging is great for freehand drawing:

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

function mouseDragged() {
  stroke(0);
  strokeWeight(3);
  line(pmouseX, pmouseY, mouseX, mouseY);
  return false;  // Prevent page scrolling
}

function draw() {
  // Don't clear background – lines persist
}

Note: pmouseX and pmouseY are last frame’s position. Drawing a line between them creates smooth strokes.

13.6 Scroll Wheel

The wheel event passes information about scrolling:

let size = 40;

function mouseWheel(event) {
  // event.delta: positive scrolling down, negative scrolling up
  size = size + event.delta * 0.5;
  return false;  // Prevent page scrolling
}

function draw() {
  background(255);
  fill(255, 100, 150);
  circle(mouseX, mouseY, size);
}

Scroll to grow or shrink the circle.

13.7 Interactive Button Example

Combine events to make buttons:

let buttonX = 200;
let buttonY = 150;
let buttonWidth = 150;
let buttonHeight = 50;
let isHovering = false;

function draw() {
  background(220);
  
  // Check if mouse is inside button
  isHovering = mouseX > buttonX && mouseX < buttonX + buttonWidth &&
               mouseY > buttonY && mouseY < buttonY + buttonHeight;
  
  // Draw button (darker if hovering)
  if (isHovering){
    fill(100);
  } else {
    fill(200);
  }
  rect(buttonX, buttonY, buttonWidth, buttonHeight);
  
  fill(255);
  textAlign(CENTER, CENTER); // TIP: centre text
  text("Click Me", buttonX + buttonWidth / 2, buttonY + buttonHeight / 2);
}

function mousePressed() {
  if (isHovering) {
    console.log("Button clicked!");
  }
}

13.8 Useful Mouse Variables

  • mouseX, mouseY – Current position
  • pmouseX, pmouseY – Previous frame’s position
  • mouseIsPressed – Boolean: is button held down?

14 Keyboard Events

Lecture 4

Just like mouse events, p5.js provides keyboard event functions that automatically run when keys are pressed or released. Keyboard input is one of the most common ways to make interactive sketches.

14.1 Keyboard Event Functions

Event When It Fires Best For
keyPressed() Every frame while key is held Continuous movement
keyReleased() When key is released Detecting key release
keyTyped() When key is typed (pressed + released) One-time key detection

14.2 Check whether key is pressed down

Use keyIsDown() to check if a specific key is currently held down:

function draw() {
  if (keyIsDown(LEFT_ARROW)) {
    console.log("Left arrow is down");
  }
}

14.3 The key and keyCode Variables

When a keyboard event fires, p5.js updates two variables:

  • key – A string: the character ('a', 'Z', '?', '9')
  • keyCode – A number: the key’s code (65 for ‘a’, 90 for ‘z’)
function keyPressed() {
  console.log("Character: " + key);     // 'a', 'M', '@', etc.
  console.log("Key code: " + keyCode);  // 65, 77, 64, etc.
}

14.4 Using key for Letters and Symbols

Use the key variable for specific characters:

let message = "";

function keyTyped() {
  if (key != ' ') {  // Ignore spacebar
    message = message + key;
  }
}

function draw() {
  background(255);
  textSize(24);
  text(message, 50, 100);
}

Compare key to specific characters:

function keyPressed() {
  if (key == 'a') {
    console.log("Pressed 'a'");
  } else if (key == 'A') {
    console.log("Pressed Shift+A");
  } else if (key == '?') {
    console.log("Pressed question mark");
  }
}

14.5 Using keyCode for Special Keys

Special keys (arrows, space, enter) don’t have characters. Use keyCode with p5.js constants:

function keyPressed() {
  if (keyCode == LEFT_ARROW) {
    console.log("Left arrow");
  } else if (keyCode == RIGHT_ARROW) {
    console.log("Right arrow");
  } else if (keyCode == SPACE) {
    console.log("Space bar");
  } else if (keyCode == ENTER) {
    console.log("Enter key");
  }
}

14.6 Common Special Key Constants

Constant Key
LEFT_ARROW
RIGHT_ARROW
UP_ARROW
DOWN_ARROW
SPACE Spacebar
ENTER Enter/Return
SHIFT Shift
CONTROL Ctrl/Cmd

14.7 Arrow Key Movement

One of the most common uses: controlling movement

let x = 200;
let y = 200;
let speed = 5;

function keyPressed() {
  if (keyCode == LEFT_ARROW) {
    x = x - speed;
  } else if (keyCode == RIGHT_ARROW) {
    x = x + speed;
  } else if (keyCode == UP_ARROW) {
    y = y - speed;
  } else if (keyCode == DOWN_ARROW) {
    y = y + speed;
  }
}

function draw() {
  background(255);
  fill(255, 100, 150);
  circle(x, y, 40);
}

Press arrow keys to move the circle.

14.8 Combining Letter and Special Keys

Use both key and keyCode in the same function:

let x = 200;
let y = 200;
let fillColor = 0;

function keyPressed() {
  // Letters for colors
  if (key == 'r') {
    fillColor = color(255, 0, 0);  // Red
  } else if (key == 'g') {
    fillColor = color(0, 255, 0);  // Green
  } else if (key == 'b') {
    fillColor = color(0, 0, 255);  // Blue
  }
  
  // Arrow keys for movement
  if (keyCode == LEFT_ARROW) {
    x = x - 10;
  } else if (keyCode == RIGHT_ARROW) {
    x = x + 10;
  } else if (keyCode == UP_ARROW) {
    y = y - 10;
  } else if (keyCode == DOWN_ARROW) {
    y = y + 10;
  }
}

function draw() {
  background(255);
  fill(fillColor);
  circle(x, y, 50);
}

15 Easing

Lecture 4

Easing is a technique for creating smooth, natural-looking movement and animations. Instead of instantly jumping an object from one position to another, easing gradually moves it towards the target. This creates animations that feel organic and polished.

Without easing, movement is jarring:

// Without easing – jumps instantly
let x = 0;

function draw() {
  background(255);
  x = mouseX;  // Instantly snaps to mouse
  circle(x, height / 2, 40);
}

With easing, movement is smooth:

// With easing – smoothly follows
let x = 0;

function draw() {
  background(255);
  x = 0.95 * x + 0.05 * mouseX;  // Gradually moves towards mouse
  circle(x, height / 2, 40);
}

The second version feels much more natural and responsive.

15.1 The Easing Formula

The core formula is:

current = factor * current + (1 - factor) * target

Or in code:

x = 0.9 * x + 0.1 * mouseX;

Breakdown:

  • 0.9 * x – keep 90% of current position
  • 0.1 * mouseX – move 10% toward target
  • Over many frames, this creates smooth approach

15.2 Basic 2D Easing

Ease X and Y separately for smooth 2D movement:

let x = 0;
let y = 0;

function draw() {
  background(255);
  
  // Ease toward mouse in both directions
  x = 0.9 * x + 0.1 * mouseX;
  y = 0.9 * y + 0.1 * mouseY;
  
  fill(255, 100, 150);
  circle(x, y, 40);
}

The circle smoothly follows the mouse around the canvas.

15.3 Comparing Different Factors

let x1 = 0;  // Fast: 0.5
let x2 = 0;  // Medium: 0.8
let x3 = 0;  // Slow: 0.95

function draw() {
  background(255);
  
  x1 = 0.5 * x1 + 0.5 * mouseX;    // Very snappy
  x2 = 0.8 * x2 + 0.2 * mouseX;    // Balanced
  x3 = 0.95 * x3 + 0.05 * mouseX;  // Floats smoothly
  
  fill(255, 0, 0);
  circle(x1, 100, 30);
  text("Fast (0.5)", x1 + 40, 100);
  
  fill(0, 255, 0);
  circle(x2, 200, 30);
  text("Medium (0.8)", x2 + 40, 200);
  
  fill(0, 0, 255);
  circle(x3, 300, 30);
  text("Slow (0.95)", x3 + 40, 300);
}

Move the mouse to see the different speeds.


16 Bounding & Constraining Values

Lecture 5

When working with dynamic values (from mouse position, keyboard input, or calculations), it’s often necessary to keep them within specific ranges. p5.js provides several techniques to constrain values. Here is an overview of their behaviour when converting a value from (0, 10) to a new range (0, 5).

Raw Constrained Modulo Map
0 0 0 0
1 1 1 0.5
2 2 2 1
3 3 3 1.5
4 4 4 2
5 5 0 2.5
6 5 1 3
7 5 2 3.5
8 5 3 4
9 5 4 4.5
10 5 0 5

16.1 Constraining with constrain()

The simplest way to constrain a value between a minimum and maximum is the constrain() function:

let x = 150;
let constrainedX = constrain(x, 0, 100);  // Result: 100

This function clamps a value: if the value is below the minimum, it returns the minimum; if above the maximum, it returns the maximum; otherwise it returns the value unchanged.

Common use case: Keep an object within canvas boundaries

let x = mouseX;
x = constrain(x, 0, width);   // Keep x between 0 and canvas width

let y = mouseY;
y = constrain(y, 0, height);  // Keep y between 0 and canvas height

16.2 Constraining with min() and max()

For more control, you can use min() and max() individually to constrain only one side:

let size = mouseX;
size = max(size, 10);         // Minimum size of 10
size = min(size, 200);        // Maximum size of 200

// Or chain them:
size = max(10, min(size, 200));  // Same as constrain(size, 10, 200)

When to use: When you only need a minimum or maximum, not both.

16.3 Wrapping with Modulo %

The modulo operator wraps values around a range, useful for looping animations or cyclic behavior:

let x = frameCount % 100;  // Loops between 0–99

Common use case: Repeating position or hue values

let hue = (frameCount * 2) % 360;  // Cycles through hue values
colorMode(HSB);
fill(hue, 100, 100);

Note: Be careful with negative values; JavaScript’s modulo behaves unexpectedly with negatives.

16.4 Mapping Values with map()

The map() function rescales a value from one range to another:

map(value, sourceMin, sourceMax, targetMin, targetMax)

This is powerful for converting input ranges to output ranges:

// Convert mouse X (0 to width) to circle size (10 to 100)
let size = map(mouseX, 0, width, 10, 100);
circle(200, 200, size);

// Convert mouse Y (0 to height) to rotation angle (0 to 360 degrees)
let angle = map(mouseY, 0, height, 0, 360);

When to use: When you need to convert one range of values to another (audio levels to sizes, accelerometer input to colors, etc.).


17 Sound in p5.js

Lecture 5

p5.js includes a sound library that allows you to synthesize sounds and play audio files. To use sound, you must first load the p5.sound library.

17.1 Setting Up Sound

  1. Download the p5.sound library from the course GitHub (in the libraries folder)
  2. Go to the libraries folder in your sketch directory
  3. Place p5.sound.min.js in that folder
  4. Add to your index.html:
<script language="javascript" type="text/javascript" src="libraries/p5.sound.min.js"></script>

17.2 Browser Audio Requirement

Browsers require user interaction before handling sound. Always call userStartAudio() in a user event (click, key press, etc.):

function mousePressed() {
  userStartAudio();
  // Now you can work with audio
}

17.3 Playing sound in p5.js

There are a couple of ways to create sound in p5.js.

17.3.1 Playing Single Notes

The simplest way is to use p5.MonoSynth() for monophonic sounds (one note at a time):

let synthesizer;

function setup() {
  synthesizer = new p5.MonoSynth();
}

function mousePressed() {
  userStartAudio();
  synthesizer.play("A4", 1, 0, 1);  // note, volume, start time, duration
}

Note formats: Use note names like "A4", "C#5", or frequencies in Hz like 440.

17.3.2 Playing Multiple Notes

p5.MonoSynth() can only play one note at a time. For chords or multiple notes, use p5.PolySynth():

let polysynth;

function setup() {
  polysynth = new p5.PolySynth();
}

function mousePressed() {
  userStartAudio();
  polysynth.play(["C4", "E4", "G4"], 1, 0, 1);  // C major chord
}

p5.PolySynth() can play multiple notes simultaneously, but can behave unexpectedly when too many notes are played at once. Try to limit the number of simultaneous notes for best results.

17.3.3 Loading Sound Files with preload()

Load sound files before the sketch starts so they’re ready to play:

let soundFile;

function preload() {
  soundFile = loadSound("path/to/sound.mp3");
}

function mousePressed() {
  userStartAudio();
  soundFile.play();
}

Sound files can be: - Local files in the same directory as your sketch - URLs from the web

17.3.4 Sound File Methods

soundFile.play();              // Play from start
soundFile.pause();             // Pause playback (can resume with play())
soundFile.stop();              // Stop and reset to beginning
soundFile.loop();              // Loop indefinitely
soundFile.setVolume(volume);   // Set volume (0 to 1)
soundFile.isPlaying();         // Check if currently playing (not mentioned in slides)

Finding free sounds: - Freesound.org - Free Music Archive - SoundBible

17.4 Microphone Input

p5.js can access the microphone to capture audio levels and create reactive visuals or instruments. Keep in mind that microphons in browsers can be a bit finicky (who hasn’t had problems with Zoom/Teams?). For the same reason, the interactive code examples in the slides may sometimes not work properly. You can still copy their code and run it yourself.

17.4.1 Capturing Audio Level

Access the microphone to respond to sound:

let mic;

function setup() {
  mic = new p5.AudioIn();
  mic.start();
}

function draw() {
  let level = mic.getLevel();  // Returns 0 to 1
  let size = map(level, 0, 0.5, 10, 100);
  
  fill(255, 100, 100);
  circle(width / 2, height / 2, size);
}

17.4.2 Smoothing Microphone Jitter with Easing

Microphone input is often jittery. Apply easing to smooth it:

let mic;
let smoothLevel = 0;

function setup() {
  mic = new p5.AudioIn();
  mic.start();
}

function draw() {
  let level = mic.getLevel();
  smoothLevel = 0.9 * smoothLevel + 0.1 * level;  // Easing formula
  
  let size = map(smoothLevel, 0, 0.3, 10, 150);
  circle(width / 2, height / 2, size);
}

18 Quick Reference

Category Function Description
Sketch Lifecycle setup() Define: runs once at start
draw() Define: runs repeatedly (default 60 fps)
preload() Define: loads files before setup()
Setup createCanvas() Create a canvas with given width and height
Shape Drawing circle() Draw a circle at (x, y) with given diameter
ellipse() Draw an ellipse (oval shape)
rect() Draw a rectangle from top-left position
point() Draw a single pixel
text() Draw text on canvas
beginShape() Start a custom shape
vertex() Add a vertex to custom shape
bezierVertex() Add a Bezier curve vertex
endShape() End a custom shape (use endShape(CLOSE) to close)
Appearance fill() Set fill colour for shapes
background() Set background colour
textSize() Set text size
strokeWeight() Set thickness of lines and points
tint() Apply colour filter to images
Images loadImage() Load image from file or URL
image() Draw image on canvas
Transformation translate() Move the origin point
rotate() Rotate around origin
scale() Scale (resize) shapes
push() Save current transformation state
pop() Restore previous transformation state
angleMode() Set angle units (DEGREES or RADIANS)
Colour color() Create a colour object
Utilities random() Generate random number
int() Convert value to integer
float() Convert value to floating-point
round() Round to nearest integer
floor() Round down
ceil() Round up
keyIsDown() Check if specific key is currently pressed
console.log() Print value to console for debugging
Constraining constrain() Clamp value between min and max
min() Return smaller of two values
max() Return larger of two values
map() Rescale value from one range to another
Sound userStartAudio() Enable audio (call in user event)
new p5.MonoSynth() Create single-note synthesizer
new p5.PolySynth() Create multi-note synthesizer
synthesizer.play() Play a note
loadSound() Load sound file from URL or local file
soundFile.play() Play sound file from beginning
soundFile.pause() Pause sound file
soundFile.stop() Stop and reset sound file
soundFile.loop() Loop sound file indefinitely
soundFile.setVolume() Set volume (0 to 1)
new p5.AudioIn() Create microphone input object
audioin.start() Start listening to microphone
audioin.getLevel() Get current audio level (0 to 1)
Events mouseReleased() Runs when mouse button is released
mouseClicked() Runs when mouse button is clicked
mouseMoved() Runs when mouse moves
mouseDragged() Runs when mouse moves while pressed
mouseWheel() Runs when mouse wheel is scrolled
keyPressed() Runs when a key is pressed
keyReleased() Runs when a key is released
keyTyped() Runs when a key is typed

19 Additional Resources