Teaching objectives
A short course in creative programming with p5.js, the JavaScript library for drawing and animating on a canvas. The student starts by watching an animated drawing that takes only a few lines of code and, room by room, learns the pieces used to write something like it: setup() and draw(), shapes, colours, variables, conditions, loops, text, custom functions and arrays. It ends with two complete sketches, a particle system and a recursive fractal tree.
The lab has 35 rooms and most follow a three-beat rhythm: an observation room, a “Test yourself” room and, for some topics, a “Write the code” room.
- Observation. A panel with the code on one side and the running canvas on the other. The student moves sliders and sees the drawing and the numbers in the code change together. The code is not edited: only values are changed.
- Test yourself. One or two multiple-choice questions and, in most rooms, a challenge to put the lines of a short program in order.
- Write the code. One or more boxes where the student types a line of code (a
call to a p5.js function, an operator, a condition, the header of a
for, a function) and it is checked. If it is right, the result appears on the canvas when the task draws something.
There are no bets. Observation rooms only ask the student to watch and try the controls; question rooms and writing rooms count as solved when the student completes everything they ask for.
Rooms by topic: 1 introduction; 2-3 setup(); 4-5 draw(); 6-7 shapes; 8-10
fill, stroke and noStroke; 11-12 RGB colours; 13-15 global and local
variables; 16-17 system variables; 18-20 if; 21-23 for; 24-25 text(); 26-28
custom functions; 29-31 arrays; 32-33 forEach, map and filter; 34-35
projects.
What you’ll learn
The student learns to read a p5.js program and to write fragments of their own, with the idea that code describes what happens in each frame.
- Knowing when each part runs:
setup()once, at the start;draw()about 60 times a second, withframeCountcounting frames (rooms 2-5). - Drawing with
ellipse,rectandlinein a coordinate system with its origin at the top left, and adding colour withfill,strokeandnoStroke(rooms 6-10). - Understanding RGB colour as three numbers from 0 to 255 (rooms 11-12).
- Telling global variables, which persist between frames, from local ones, which
are reset on every call, and using
+=,-=and*=(rooms 13-15). - Using what p5.js already knows about its environment:
mouseX,mouseY,width,height,mouseIsPressed(rooms 16-17). - Deciding with
ifand telling=from==(rooms 18-20). - Repeating with
forand recognising its three parts (rooms 21-23). - Writing text on the canvas with
text(),textSize()andtextAlign()(rooms 24-25). - Defining functions with parameters and with
return(rooms 26-28). - Storing many values in an array, adding and removing elements, and going through
it with
for...of,forEach,mapandfilter(rooms 29-33). - Putting it all together in two longer sketches, with arrays, functions and recursion (rooms 34-35).
Key mathematical ideas
This is a programming lab; the mathematical ideas are the ones that underlie the drawing.
- Coordinates. The canvas is a Cartesian plane with its origin at the top-left corner and the y axis pointing down; an ellipse or a rectangle is determined by position and size.
- RGB colour. Each colour is a triple (R, G, B) with values from 0 to 255; (255, 0, 0) is pure red and (0, 0, 255) pure blue.
- Remainder of a division. Room 4 uses
x % 400: whenxreaches 400, the remainder goes back to 0 and the ball starts again from the left. - A variable as state. A counter that grows on every frame and is compared with a limit (the bouncing ball in room 18).
- Conditions and comparisons. The operators
>,>=,==,!=;=assigns and does not compare. - Iteration. A
forwith a start, a condition and a step draws a row of circles with fixed spacing; changing the step, the start or the end changes how many there are. - A function as a transformation. A function with
returngives back a value (cubo(3)is 27);mapapplies a function to every element of an array. - Indices. An array starts at index 0: with
lista = [1, 2, 3],lista[1]is 2 andlista.indexOf(3)is 2. - Recursion. In the tree of room 35,
rama(len, n)calls itself with a shorter length (the ratio) and one level fewer, and stops whennreaches 0;push()andpop()save and restore the position.
Classroom notes
- The student does not write a complete program: the writing tasks are one or two lines and the rest is moving controls, choosing options and ordering lines. It is a first approach to p5.js, not a free programming workshop.
- The writing challenges check what was typed (the function and its arguments, or the form of the expression) and show an example when it fails; the teacher can see what the student tried, including failed attempts.
- The questions on
if (x = 5), which assigns instead of comparing and gives no warning in JavaScript, and on local versus global variables are the points where it is worth stopping with the class. - The last two rooms work well projected: the student changes gravity, bounce, angle or depth and relates each parameter to the line of code.
Level: Bachillerato (Informatics). No prior programming knowledge is needed. The
language is JavaScript; room 20 points out that, unlike Python, the parentheses of
an if are mandatory.
Room-by-room contents
Room 1 · Just a few lines… and this happens
An animated drawing of five rings of coloured circles turning around the centre, with a code panel beside it. The drawing has 6-fold symmetry and takes only a few lines: two nested loops, an angle a = TWO_PI / n * i and the colour depending on the position. Moving the mouse over the canvas changes the speed of rotation. It is an opening room, with no questions: the student just looks at the result and at the code that produces it, which will be learned in the following rooms.
Student tasks
- Move the mouse over the canvas and see how the rotation speed changes.
- Read the code that generates the drawing.
Room 2 · setup(): prepare the canvas
A panel with the code of setup() and the canvas it creates. Three sliders change the width, the height and the background colour, and the code updates at the same time, highlighting the number that changed. The idea is that setup() runs only once, at the start, and is where the canvas size and the background are decided. It is observation only.
Student tasks
- Move the width, height and background sliders.
- See how the canvas and the arguments of createCanvas() and background() change together.
Room 3 · Test yourself: setup()
Two multiple-choice questions about the previous room: how many times setup() runs (60 times a second, only once, only on click or whenever the mouse changes) and which function creates the canvas (drawCanvas(), createCanvas(), makeCanvas() or newSketch()). A hint is given when the answer is wrong.
Student tasks
- Choose how many times setup() runs (only once).
- Choose which function creates the canvas (createCanvas()).
Room 4 · draw(): the magic of motion
A circle moves across the canvas with an x that grows with frameCount. A slider (1 to 8) changes the speed. draw() runs 60 times a second and frameCount goes up by 1 each time. A note explains the % operator: it is the remainder of a division (7 % 3 = 1, 10 % 4 = 2) and, when x reaches 400, it goes back to 0 and the ball starts again from the left.
Student tasks
- Change the speed and see the effect on the motion.
- Read the note on the remainder of a division and relate it to the ball that goes back to the start.
Room 5 · Test yourself: draw()
Two multiple-choice questions: where the code that is animated continuously goes (in setup(), in draw(), outside both functions or anywhere) and which p5.js variable counts the frames since the start (frameNumber, frameCount, counter or tick).
Student tasks
- Choose where the animated code goes (in draw()).
- Choose which variable counts the frames (frameCount).
Room 6 · Basic shapes: ellipse, rect, line
A canvas with an ellipse, a rectangle and a line, and the code of the three calls: ellipse(x, y, width, height), rect(x, y, width, height) and line(x1, y1, x2, y2). Four sliders change the position and size of the ellipse. All coordinates are measured from the top-left corner, which is (0, 0). It is observation only.
Student tasks
- Move the sliders for the ellipse’s x, y, width and height.
- Notice how each number in the code changes the drawing.
Room 7 · Test yourself: basic shapes
Two multiple-choice questions (which function draws a rectangle: square(), rect(), rectangle() or box(); and how ellipse is written, with its four arguments) and a challenge to order the lines of a draw() that paints a 100 × 60 ellipse at (200, 150) on a grey background. Idea: ellipse(x, y, width, height) takes the position and the size.
Student tasks
- Choose the function that draws a rectangle (rect()).
- Choose the signature of ellipse (ellipse(x, y, width, height)).
- Order the four lines of draw() to draw an ellipse on a grey background.
Room 8 · fill(), stroke(), noStroke(): colouring shapes
A circle with a fill and an outline, and the code with fill(), stroke() and noStroke(). Three sliders change the fill colour in R, G and B (each from 0 to 255) and another, strokeWeight, the outline thickness; at 0 the outline disappears. fill() paints the inside, stroke() the outline and noStroke() removes it. It is observation only.
Student tasks
- Move the R, G and B sliders of the fill.
- Change the outline thickness, down to 0, and see the effect.
Room 9 · Test yourself: fill and stroke
Two multiple-choice questions (which function removes the outline and which sets the fill) and a challenge to order six lines of a draw() that draws a red circle with no outline: background(200), fill(255, 0, 0), noStroke() and circle(200, 150, 80).
Student tasks
- Choose the function that removes the outline (noStroke()).
- Choose the function that sets the fill (fill()).
- Order the lines to draw a red circle with no outline.
Room 10 · Write the code: colour and shapes
The first room in which the student writes code. Three tasks, each with a text box: paint the fill red, draw a rectangle at given coordinates (x = 20, y = 40, width 80, height 30) and remove the outline. It is checked that the function and the arguments match; if it is right, the result appears on the canvas, and if not, an example is shown.
Student tasks
- Write fill(255, 0, 0);.
- Write rect(20, 40, 80, 30);.
- Write noStroke();.
Room 11 · RGB colour space: (R, G, B)
A canvas with the chosen background colour and a circle in the inverted colour, with three sliders, one per channel (0 to 255). Each colour is defined with three numbers: red (R), green (G) and blue (B). Moving the sliders shows how the channels mix and how the code changes. It is observation only.
Student tasks
- Move the R, G and B sliders and see how they mix.
- Notice the inverted colour of the circle.
Room 12 · Test yourself: RGB colours
A challenge with sliders: obtain pure red, with a target colour and the current colour shown side by side. Then a multiple-choice question: which colour fill(0, 0, 255) produces (green, blue, red or white). Idea: the third number is the blue channel and 255 is the maximum intensity.
Student tasks
- Adjust the sliders until pure red is obtained (R = 255, G = 0, B = 0).
- Choose the colour fill(0, 0, 255) produces (blue).
Room 13 · Global and local variables
A sketch where x is a global variable, which persists between frames and grows, and radio is a local variable declared inside draw(), which is reset on every call. A slider, vel, sets the speed, and a counter shows the value of x. Idea: what is declared outside the functions persists between frames; what is declared inside starts from zero on each call.
Student tasks
- Change the speed and follow the value of x.
- Compare the global variable x with the local radio, which is reset.
Room 14 · Test yourself: variables
Two multiple-choice questions: what value let contador = 0 declared inside draw() has at frame 100 (0, because it is reset on every call) and what vel += 2 does (adds 2 to vel, the same as vel = vel + 2). And a challenge to order a program with two global variables and a draw(); the order of the two variables and of the two lines inside draw() can be swapped and all the valid orders are accepted.
Student tasks
- Choose what value a local variable has at frame 100 (0).
- Choose what vel += 2 does.
- Order the code: globals first and then draw().
Room 15 · Write the code: modify variables
Two tasks with assignment operators on given variables: subtract 20 from ancho and double alto. Both the compound and the expanded forms are accepted (ancho -= 20 or ancho = ancho - 20; alto *= 2 or alto = alto * 2). When it is right, the rectangle on the canvas changes size.
Student tasks
- Write a line that subtracts 20 from ancho (ancho -= 20).
- Write a line that doubles alto (alto *= 2).
Room 16 · Variables p5.js gives you for free
A canvas with a circle that follows the mouse with mouseX and mouseY, another fixed in the centre with width/2 and height/2, and a text with the mouse coordinates. These are variables p5.js updates by itself. The room shows them by moving and clicking the mouse; there are no sliders.
Student tasks
- Move the mouse and watch the coordinates and the circle that follows it.
- Click and relate what is seen to mouseIsPressed and the other variables.
Room 17 · Test yourself: system variables
Two multiple-choice questions: which variable holds the horizontal position of the mouse (mouseY, mouseX, posX or xMouse) and which one is true while the mouse is pressed (mouseDown, mouseIsPressed, isClicked or pressed).
Student tasks
- Choose the variable for the horizontal mouse position (mouseX).
- Choose the variable that is true while the mouse is pressed (mouseIsPressed).
Room 18 · if: making decisions in code
A ball bounces because two if conditions detect when it reaches the edge; the code is beside it. A slider (1 to 10) changes the speed and a click on the canvas changes the background colour. Idea: an if runs a block only when the condition is true, and with two of them the velocity is reversed at the ends.
Student tasks
- Change the speed and watch the bounce.
- Click on the canvas to change the background.
- Relate each if in the code to the edge it detects.
Room 19 · Test yourself: if
Three challenges. First a trick question: what if (x = 5) runs, which checks nothing but assigns 5 to x and almost always enters the block, without JavaScript giving any warning. Then, marking the comparison operators among six (>=, =>, ==, !=, = and >; the comparisons are >=, ==, != and >). Finally, ordering the lines of a bounce at the right edge.
Student tasks
- Choose what if (x = 5) does (assigns 5 to x and almost always enters).
- Mark the comparison operators (>=, ==, !=, >).
- Order the lines of the bounce at the right edge.
Room 20 · Write the code: if conditions
Two tasks to complete the condition of an if, shown as if ___ { ... }: “x is greater than alto” and “x is equal to y”. The condition is written with parentheses, which in JavaScript are mandatory (unlike Python), and the second one forces the student to tell == from =. Both == and === are accepted.
Student tasks
- Write (x > alto).
- Write (x == y).
Room 21 · for: repeat without copy-paste
A single for loop draws a row of circles. Three sliders change the step (x +=), the start (x =) and the end (x <=) of the loop, and the code updates. Idea: a for repeats its body while the condition holds and advances the variable on every pass, so the step, the start and the end determine how many circles there are and where.
Student tasks
- Move the step, start and end sliders.
- Watch how the number and the spacing of the circles change.
Room 22 · Test yourself: for loop
A canvas with the loop for (let x = 30; x <= 370; x += 40) that draws a row of circles. One multiple-choice question with four answers about the loop (the initial value of x, the spacing between circles, how far x goes and the circle’s Y position) and a challenge to order the lines that draw the row. Idea: the loop step sets the spacing between circles.
Student tasks
- Choose the correct answer about the loop (the spacing between circles).
- Order the lines to draw a row of circles.
Room 23 · Write the code: the three parts of for
The for loop has three parts separated by ;: initialisation, condition and increment. The student fills in each in a box, with the rest of the loop in view. Equivalent forms are accepted, for example x < 400 or x <= 350 for the condition.
Student tasks
- Write the initialisation (let x = 0).
- Write the condition (x < 400 or x <= 350).
- Write the increment (x += 50).
Room 24 · text(): writing on the canvas
A large centred text and a small one with a variable, drawn with text("string", x, y). Three sliders change textSize, the x position and the y position, and the code updates; textAlign() is also used. It is observation only.
Student tasks
- Move the sliders for text size and x and y position.
- See how the text and the arguments of text() change together.
Room 25 · Test yourself: text()
Two multiple-choice questions (which function draws text on the canvas, print(), text(), write() or draw(); and what textAlign(CENTER, CENTER) does: centres the text horizontally and vertically) and a challenge to order the lines of a draw() that shows “Hola p5.js!” in white on a dark background.
Student tasks
- Choose the function that draws text (text()).
- Choose what textAlign(CENTER, CENTER) does.
- Order the lines to show the text.
Room 26 · Your own functions: flor(x, y)
A function flor(x, y) defines a flower with a central circle and four petals, and it is called twice with different coordinates. Four sliders move each of the two flowers. Idea: a function is defined once and used as many times as wanted, with parameters that change what it draws.
Student tasks
- Move the sliders for each flower’s coordinates.
- Compare the code of the definition with the two calls.
Room 27 · Test yourself: custom functions
A multiple-choice question about what has to be done before calling flor(100, 150) (define function flor() before using it) and a challenge to order the lines of the definition of flor(x, y), with a central circle and two petals.
Student tasks
- Choose what is needed before calling a custom function (defining it).
- Order the lines to define flor(x, y).
Room 28 · Write the code: define functions
Two tasks: define a function nada that does nothing (with the full syntax and braces) and define cubo(n) that returns n cubed. The second is checked by running the function: cubo(3) must give 27. return makes the function give back a value that can be used wherever it is called; without return it gives back undefined.
Student tasks
- Write function nada() {}.
- Write a function cubo(n) that returns n · n · n.
Room 29 · Arrays: storing many values
An array stores several elements in a single variable. Clicking on the canvas adds a point with push(), and a for...of draws each one; a label shows the array’s length. Two buttons remove the last point (pop()) or empty the array ([]).
Student tasks
- Click on the canvas to add points with push().
- Remove the last point with “pop” and empty the array with the corresponding button.
Room 30 · Test yourself: arrays
Two multiple-choice questions (what xs.push(50) does and how to access the first element of xs: xs[0]) and a challenge to order the lines of a program that declares an array, adds a position and draws it with for...of. Idea: indices start at 0.
Student tasks
- Choose what push() does (adds an element at the end).
- Choose how to access the first element (xs[0]).
- Order the lines: declare the array, add an element and draw it.
Room 31 · Write the code: array operations
Starting from let lista = [1, 2, 3]. Four tasks: what lista[1] is (2), what lista.indexOf(3) is (2), what lista.length is after running lista.pop() (2) and writing the code that adds a 7 at the end. The last one is checked by running what is written. The first three are answered with a number.
Student tasks
- Write the value of lista[1] (2).
- Write the value of lista.indexOf(3) (2, the index where the 3 is).
- Write the value of lista.length after lista.pop() (2).
- Write the code that adds the 7 at the end of lista (for example lista.push(7)).
Room 32 · forEach, map, filter: working with arrays
A bar chart with the values of an array. forEach draws all the bars in blue, filter highlights in red those above a threshold and map creates a new array with double each height. A slider moves the threshold of filter (from 20 to 170) and the bars that end up highlighted can be seen. It is observation only.
Student tasks
- Move the threshold and see which bars filter selects.
- Compare the three calls, forEach, map and filter, with what they draw.
Room 33 · Test yourself: forEach, map, filter
Two multiple-choice questions (what map() returns, a new array with the transformed values; and what filter() is for, returning the elements that meet a condition) and a challenge to order the lines of a program that declares an array and uses forEach, map and filter on it.
Student tasks
- Choose what map() returns (a new array).
- Choose what filter() is for.
- Order the lines of the program with forEach, map and filter.
Room 34 · Project: particle system
A complete sketch that brings together arrays, for...of, if, functions and variables: an array of particles that fall under gravity and bounce on the floor and the sides. Four sliders adjust gravity, bounce, number of particles and colour (hue), and each click on the canvas launches 8 more particles. The code updates as the controls are moved. There are no questions: it is about handling a longer program and reading how it is built.
Student tasks
- Adjust gravity, bounce, number of particles and colour.
- Click on the canvas to launch more particles.
- Find in the code the array, the loop and the conditions.
Room 35 · Project: recursive fractal tree
A tree drawn with a function rama(len, n) that calls itself: each branch splits into two shorter ones, turned right and left, until the level n reaches 0. push() and pop() save and restore the position. Four sliders change the angle, the depth, the reduction ratio (in %) and the trunk length. There are no questions: it is about handling a longer program and reading how it is built.
Student tasks
- Change the angle, depth, ratio and length and see how the tree changes.
- Find in the code the call of rama to itself and the push()/pop().
Rooms to project
The most striking ones to show and discuss in class.