Anatomy of a (p5.js) Sketch
Disclaimer: This is not JavaScript (JS) 101. To get started with JS please read these articles on MDN.
- "JavaScript basics - Learn web development | MDN"
- "JavaScript | MDN"
- "JavaScript Guide - JavaScript | MDN"
This article is focused on getting you up and running with p5.js
Sketch Building Blocks
The most common building blocks of a sketch are setup, draw and the global and local scope. Below you see a very basic sketch that
actually does nothing. Roll over the different areas to visualize their scope.
const globalVariable = 123;
function setup() {
const scopedSetupVariable = "Only in setup";
console.log(globalVariable); // 123
}
function draw(){
console.log(globalVariable); // 123
console.log(scopedSetupVariable); // undefined
const scopedDrawVariable = "Only in draw";
if(true){
let blockScopedVariable = "Only in block!"
console.log(scopedDrawVariable); // Only in draw
}
console.log(blockScopedVariable); // undefined
}
Scopes (global and local)
In JavaScript (JS) and other programming languages we encounter the idea
of scopes. That means something like a variable is bound to a specific area of
the code. Functions can have their own scope, there are block scopes and
there is something called the global scope where everything exists
inside. We can even declare our own scopes in JS by wrapping some part
of the code into curly brackets.
Why is this useful? For one thing it allows us to reuse variable names
without overwriting other parts of the program. We can define which
variables are available to which part of the program. It also enables
use to write more conscious code that does not leak into other areas of
the program. Code that has less sideeffects. To make sure that we use
the scoping features of JS we always should use const
and let to define our variables, never var.
let is for variables that change over the course of our programm,
const is for constant variables. var is
the old way to declare variables and should be avoided since they
are not scoped. To learn more about the difference take a look at this article:
"Grammar and types - JavaScript - Variable scope | MDN"
When you write p5.js sketches you declare the things you want to use
throughout your programm in the global scope at the top. When something
is only needed in the setup, the draw, another
function, or even the block scope of an if or for statement you declare them inside of that scope (the curly brackets ). To sum this up.
- Scopes define where variables are accessible in code
-
Types of scopes:
- Global scope
- Function scope
- Block scope
-
Benefits of scoping:
- Reuse variable names without conflicts
- Control variable accessibility
- Write more conscious code with fewer side effects
-
Best practices:
- Use
constfor constants - Use
letfor variables that change - Avoid
var(not scoped properly)
- Use
-
In p5.js:
- Declare global variables at the top
- Use local scopes in functions and blocks as needed
setup()
The setup is the area of your code where you initilze things,
like calculating a value you need or setting up 1000 particles for your program.
You name it. The setup is executed once you load the page/sketch and the
code inside will be executed from top to bottom. Of course you can also ue
drawing commands in the setup but you won't be able to make things interactive.
As the function name says you set
things up.
You should not try to load other resources like images, fonts or data
from some file in the setup. p5.js has a specilized
function for that. The preload. This function will run
before your setup and draw and will also wait until
all things are done loading.
-
setup()function:- Initializes program elements
- Executes once when page/sketch loads
- Runs from top to bottom
- Can include drawing commands, but not for interactive elements
-
Loading resources:
-
Avoid loading resources (images, fonts, data) in
setup() -
Use
preload()function instead:-
Runs before
setup()anddraw() - Waits for all resources to load
-
Runs before
-
Avoid loading resources (images, fonts, data) in
draw()
The draw (also as the name says) is there for drawing and making
things interactive. It is executed all the time and the code inside will
also be evalutated from top to bottom. The speed of the program depends on
many things. The resources of your computer and of course the amount of calculations
that need to be done. You could see the draw as the main function of your
program where all things should flow together.
Due to JavaScript's asynchronous nature and the browser's event-based system, drawing and interactivity can also occur in other functions. This is a more advanced topic for later discussion.
-
draw()function:- Main purpose: drawing and interactivity
- Executes continuously
- Code inside runs from top to bottom
- Can be considered the main function of the program
-
Program speed factors:
- Computer resources
- Amount of calculations required
-
Advanced note:
- Drawing and interactivity can occur in other functions
- Due to JavaScript's asynchronous nature
- Related to browser's event-based system
Other Magic Functions
Both of these functions are somehow magically executed. There are also
others like these. For example there is the mousePressed function
that is executed everytime the mouse is pressed. (duh!). Or the preload function as already mentioned above. To use these you declare them besides
your setup and draw in the global scope. (There
are even more of these functions but we won't list them all here.)
function preload()
function setup()
function draw()
function mousePressed()
Besides these "magic" functions that are declared in global scope
and executed automagically there are other functions we use to manipulate
or generate things. For example there is the random functio
which generates random numbers for us. Or the createCanvas function,
that allows us to manipulate the size of the canvas where we draw onto. The
point, rect, circle, line functions to draw primitive shapes. And so on. There are many specialized
functions that we will have to discuss and learn. The most important thing
is. The names of these function are some kind of reserved by p5.js. They
wont throw an error in the browser console when we try to use them in a different
way (not like reserved words for JS). At least p5.js will give you a friendly error when you do that.
-
Some "Magic" Functions in p5.js:
-
preload(): Loads resources, waits until everything is loaded setup(): Initializes things, executes once-
draw(): Used for drawing, executes continuously -
mousePressed(): Event-based, executes on mouse press - Declared in global scope and executed automatically
-
-
Some Other p5.js Functions:
random(): Generates random numberscreateCanvas(): Sets canvas size-
point(),rect(),circle(),line(): Draw primitive shapes - Many specialized functions available
-
Important Note:
- Function names are reserved by p5.js
- Using them differently may not throw browser console errors
- p5.js provides friendly errors for misuse
Sketch
In the above sketch we make use of many concepts we discussed above. We
declare the global mutable variables x and y both
with the value 50 in the global scope so they are both available
in setup and draw.
Within the setup we use createCanvas function to
create drawing area with a size of 100 pixel width and
100 pixel height and assign it to the immutable const variable
canvas. Then we call a function of the canvas object canvas.parent() to put it into the right element in the DOM. The element with the attribute
id set to sketch. Then we set 3 things for the
drawing context of p5.js. The background for the canvas. The
stroke color and the fill color. The stroke and fill affect all elements we create after these command
is called until we reset them again. The context of p5.js keeps track of
these information which colors to use. These values will be valid also in
the draw function.
In the draw we declare two immutable locallly scoped variables
called previousX and previousY and store the current
value x and y before we reassign them with a random value in the range between 10 and
90. This allows us to draw a line from the previous
x/y value to the newly calculated one. After the line we draw a circle at x/y with a radius of 10 pixels.
-
Global scope:
-
Mutable variables
xandydeclared with value 50
-
Mutable variables
-
setup()function:-
Creates canvas (100x100 pixels) using
createCanvas() - Assigns canvas to DOM element with id "sketch"
-
Sets drawing context:
- Background color
- Stroke color
- Fill color
-
Creates canvas (100x100 pixels) using
-
draw()function:-
Declares local variables
previousXandpreviousY -
Updates
xandywith random values (10-90) - Draws a line from previous to new position
- Draws a circle at new position (radius 10 pixels)
-
Declares local variables
-
Context management:
-
Stroke and fill colors set in
setup()persist indraw()
-
Stroke and fill colors set in
Tasks
- Clone this sketch with the button below (save it to your account)
-
Try to create global mutable (
let) variables to change colors - Remember you can find named colors that you can use here
-
Create locally scoped variables to hold things like the radius and
alter them using
random