Rainbow Paintbrush in p5.js

Photo by Ricardo Gomez Angel on Unsplash

This tutorial will show you how to make a paint canvas in p5.js with a rainbow brush (and keyboard controls)! Written for a session at CC Fest LA, this tutorial is geared towards those new to p5.js but familiar with basic coding concepts (functions, variables, conditionals).

If you get errors in your code along the way: double-check for typos, refer to one of the gists in the tutorial, or check out the solution code!

Setup

Getting set up in p5.js has never been easier thanks to the p5.js web editor! Open up the editor and identify the setup and draw functions:

The setup function contains code that will just run once, at the start of your program. The draw function will execute over and over again. Right now, the line of code background(220) draws a light grey background repeatedly.

We don’t want our work to be constantly covered up by a light grey background, so we are going to comment out that line of code! We can comment out code by adding two slashes in front of it: //.

Comment out line 6!

The computer ignores comments; they are mostly there for us humans to read (if, for instance, we want to remember how to draw a background again).

Code so far:

Dragging the mouse

In a typical computer paint canvas, we drag our mouse on the screen and the computer leaves paint in its wake. Therefore, we need our program to recognize when the mouse is being dragged.

p5.js already has built-in functionality for this. Along with the built-in functions of setup and draw, there is also the mouseDragged function.

At the bottom of your code, add in this new function:

function mouseDragged() {
}

Right now, this function has no code inside and does nothing! Inside it, we need to write what we want the computer to do when the mouse is dragged.

When the mouse is dragged, we want the computer to put paint on the screen. To get more technical, we want the computer to draw an ellipse (specifically, a circle) at the exact (x, y) location of our mouse.

p5.js has built-in variables to detect the coordinates of your mouse: mouseX and mouseY. Inside the mouseDragged function, we will use those variables to draw an ellipse. Update your mouseDragged function to include this code:

function mouseDragged() {
ellipse(mouseX, mouseY, 50, 50);
}

This code will draw a 50x50 ellipse (effectively a circle) at the (x, y) location of your mouse. Try it out and see if it works!

Woohoo!

Still, these plain circles with their black outline are not as glorious as the rainbow I promised you. Let’s fix that.

First, you can make your circles a different color using the fill function. Insert a line of code to make the fill of your circles grey:

function mouseDragged() {
fill(220);
ellipse(mouseX, mouseY, 50, 50);
}

You may recall from when we commented out the background that 220 is the code for light grey. We’ll be doing a deep dive into colors in the next section.

Run your code and see if your circles turn light grey!

Now let’s get rid of that black outline (not super aesthetically pleasing, in my opinion). Add in a call to the noStroke function to clean that up:

function mouseDragged() {
noStroke();
fill(220);
ellipse(mouseX, mouseY, 50, 50);
}

Now you should have a subtle light grey paintbrush!

“But Kelly,” you are saying, “I did not want a subtle light grey paintbrush. I wanted a RAINBOW!”

That is correct. Let’s add some rainbow!

Code so far:

Making the rainbow paintbrush

Before we actually add the rainbow, we need a cursory understanding of HSL colors. Although you can also tell the computer colors in formats such as hexadecimal and RGB, HSL allows you to describe the color in terms of Hue, Saturation, and Lightness.

Hue is a degree from 0–360 on the color wheel. Saturation is the vividness of the color, expressed in a percent: 0% would be grey, and 100% would be full color. Lightness is also expressed in a percent: 0% would be dark and 100% would be light.

To make a rainbow paintbrush, we need to use HSL colors because it allows us to cycle really easily through all the hues on the color wheel, from 0 to 360 over and over again.

First thing, we need to inform the computer that we are talking about colors in HSL, not RGB or hex!

Update your mouseDragged function to use HSL by calling the colorMode function:

function mouseDragged() {
colorMode(HSL, 360);
noStroke();
fill(220);
ellipse(mouseX, mouseY, 50, 50);
}

Now we can use HSL colors in our fill function! Instead of 220 (for our original grey), we will need to supply three numbers. I’m going to use 0 for the hue (starting us off with red), 200 for the saturation (yes, it’s a percentage, but I like things REALLY colorful), and 200 for lightness.

Change your fill(220) line to fill(0, 200, 200) so that your code now looks like this:

function mouseDragged() {
colorMode(HSL, 360);
noStroke();
fill(0, 200, 200);
ellipse(mouseX, mouseY, 50, 50);
}

Now try out your paintbrush! Is it red?

Now we just need the brush to cycle through all the degrees of the color wheel, from 0 to 360 (and over again)! To do this, we’re going to create a variable to store our current hue so that we can continuously increment it.

At the very top of your code (before even the setup function) declare a variable called hue:

var hue;

Now, inside the setup function, add a line that sets the hue variable to 0:

function setup() {
createCanvas(400, 400);
hue = 0;
}

Your complete code should now look like this:

Returning the mouseDragged function, add a line at the top that increments the hue variable by one:

function mouseDragged() {
hue++;
colorMode(HSL, 360);
noStroke();
fill(0, 200, 200);
ellipse(mouseX, mouseY, 50, 50);
}

If you haven’t seen the syntax before, hue++ is equivalent to saying hue = hue + 1.

Now, to use our hue variable (it’s just incrementing in a vacuum right now), we need to use it in our fill line.

Change your fill line to read fill(hue, 200, 200) so that our paintbrush constantly changes color!

Your mouseDragged function should now read:

function mouseDragged() {
hue++;
colorMode(HSL, 360);
noStroke();
fill(hue, 200, 200);
ellipse(mouseX, mouseY, 50, 50);
}

And your paintbrush should be writing in rainbow!

It’s modern art!

Code so far:

A small catch

However, if you paint for long enough, you may notice… your paintbrush starts only writing in red!

This happens because we increment the hue variable up to 360… and then keep going! At that point, the brush gets stuck on the hue of 360 (red).

We need to make a more sophisticated system for incrementing the hue variable. If hue is greater than 360, we should reset it to 0, but otherwise proceed as normal.

Replace the incrementation line, hue++, with this conditional:

if (hue > 360) {
hue = 0;
} else {
hue++;
}

Now test out your code. Is it drawing beautiful rainbows?

Or at least squiggly rainbow modern art?

Success!

Code so far:

Ternary operator (optional)

Are you in your coding panic zone right now? If so, skip to the mini-challenges!

If not… do you thirst for eloquent JavaScript syntax? You can refactor the conditional we just wrote into a beautiful single line of code using the ternary operator.

Replace your conditional with this line of code:

hue > 360 ? hue = 0 : hue++;

Translated to English, this says: “Is hue greater than 360? If so, reset hue to 0. Otherwise, increment it.”

Mini-challenges

Congratulations on getting the rainbow canvas up and running! (If yours isn’t working, compare your code with the latest code gist above — or just copy and paste the gist into your editor so you can play around with it.)

Can you figure out how to…

  1. Make the brush a different size?
  2. Make the colors change more quickly?
  3. Make the background black and the canvas the width and height of the window? (super challenge)

Mini-challenge solutions

Try to tackle at least the first two challenges before reading the solutions!

  1. Change the dimensions of the ellipse drawn on the screen in your mouseDragged function. Instead of a 50x50 ellipse, you could have a smaller 25x25 ellipse with this line of code: ellipse(mouseX, mouseY, 25, 25);
  2. Instead of incrementing hue by one with the code hue++, you could increment hue by a larger number. For instance, to increment by 10 (and make the color change 10x as fast), you would write: hue = hue + 10 or simply hue += 10.
  3. You have to go to the setup function to set the background without erasing your beautiful rainbow art. This setup function uses the built-in variables windowWidth and windowHeight to make the canvas take up the whole window and then 0 as a color code for the black background:
function setup() {
createCanvas(windowWidth, windowHeight);
background(0);
hue = 0;
}

Adding keyboard controls

Our rainbow canvas is very cool, but most computer paint editors allow you to pick certain colors, like red, green, blue, etc.

We are going to add some extra functionality to change colors via the keyboard! For instance, when you press “r,” the color will turn to just red.

Just like we used the mouseDragged function to react to the mouse dragging across the screen, we can also use the keyPressed function to react to keyboard activity.

Add this empty function at the bottom of your code:

function keyPressed() {
}

All the keys on the keyboard have different codes associated with them. For instance, “r” is 82 — and I know this because I typed “r” in the handy key code website!

Inside our keyPressed function, we want to check if the key code matches up to “r.” For testing purposes, I’ll have my computer give me an alert when I press the “r” key.

Update your keyPressed function to include this code:

function keyPressed() {
if (keyCode == 82) {
alert("You typed the letter r!");
}
}

Paint a little, then type “r” and see if the alert pops up!

OK, now that we know the program is detecting our keyboard strokes, let’s make the brush turn to red when we press “r.” We can make the brush red by setting the hue variable back to 0.

(Side note: you may noticed that in an earlier context, 0 was the code for the black. This was because we were using a shorthand for RGB colors. Here, 0 is red because we are using HSL colors.)

Instead of issuing an alert, update your keyPressed function to change hue to 0 when the “r” key is pressed:

function keyPressed() {
if (keyCode == 82) {
hue = 0;
}
}

Now try out your code! Paint a little rainbow, then press “r,” then paint again. What happens?

It’s still just painting rainbow! Whaaaat?!?!

If you look really closely, you’ll see that after you press “r” and paint again, the brush starts out as red — but then rapidly starts cycling through the rainbow again. How do we turn it off?

Code so far:

Turning off the rainbow

If we want our paintbrush to stay red, we need to turn off the incrementation that creates the rainbow.

To track the status of whether the rainbow functionality is on or off, we can add in a variable at the very top of code (along with the hue variable).

At the top of your code, initialize a variable called rainbow as true:

var rainbow = true;

By default, the rainbow functionality is on, so this variable is true.

When the “r” key is pressed, we want to turn the rainbow OFF. This will involve setting the rainbow variable to false. Go into your keyPressed function and make rainbow false when you press “r.”

Your updated keyPressed function should look like this:

function keyPressed() {
if (keyCode == 82) {
hue = 0;
rainbow = false;
}
}

Finally, we need to only increment hue when the rainbow is on (when the rainbow variable is true).

Returning to our mouseDragged function, we can wrap our incrementation code in a conditional like so:

if (rainbow) {
if (hue > 360) {
hue = 0;
} else {
hue++;
}
}

Now, the incrementation code will only execute if the rainbow is on. Saying if (rainbow) is the same as saying if (rainbow == true).

Try out your new code! When you press “r,” does the paintbrush stay red?

Woohoo!

Code so far:

Mini-challenges

You will need the key code site and also the HSL color picker to tackle these challenges.

  1. Make the brush turn green when you hit “g.”
  2. Make the brush turn blue when you hit “b.”
  3. Reset the brush to rainbow when you hit the space bar.
  4. Make the brush paint rainbow at a faster rate each time you hit the space bar.

Solutions

Try to tackle each challenge to the best of your ability before reading the solutions!

  1. The key code for “g” is 71, and a good green hue is 125. Include this conditional in the keyPressed function:
if (keyCode == 71) {
hue = 125;
rainbow = false;
}

2. The key code for “b” is 66, and a good blue hue is 200. Include this conditional in the keyPressed function:

if (keyCode == 66) {
hue = 200;
rainbow = false;
}

3. The key code for the space bar is 32. Include this conditional in the keyPressed function:

if (keyCode == 32) {
rainbow = true;
}

4. Create a variable called rate (near the top of your code, outside any functions) to keep track of the rate of change; initialize it with the value of 1. Inside the mouseDragged function, increment hue by rate rather than a static number: hue += rate; Finally, update your conditional for key code 32 so that it doubles the rate variable at every tap of the space bar:

if (keyCode == 32) {
rainbow = true;
rate *= 2;
}

Final product

Congratulations for building a beautiful rainbow canvas!

Here is the full solution code, complete with solutions to the mini-challenges as well.

What else can you add to the canvas? (Ideas: make key press responses for erasing the board, changing the brush size…) What else can you make with p5.js?