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
const
for constants - Use
let
for 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
x
andy
declared 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
previousX
andpreviousY
-
Updates
x
andy
with 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