# How to Paint with Code: Creating Your Brushes

There's a world of possibility in designing our own digital painting tools that we can't easily recreate with analog tools like a normal pencil. In this guide, we'll explore the many ways we can code our own paintbrushes using a Javascript creative coding library called p5.js.

We'll start by creating a basic line drawing application, which will be our template going forward to create a whole toolkit of distinct paintbrushes. We'll mix and match a variety of painting techniques, along with a bit of math, and see what textures emerge in the process.

To get the most out of this tutorial, you should be familiar with the fundamental concepts of Javascript, such as functions, `for`

loops and arrays. However, you won't need any previous experience with the p5.js library so beginners are welcome!

Okay, enough chitchat — let's start painting!

## Drawing a simple line

Let's start off with the basics by creating a simple line-drawing tool with p5.js. You can use this code as a starter template and testing playground for any other paintbrushes you make with p5.js.

First, try it out yourself! Click and drag on the canvas below to draw a line.

## A brief breakdown of our starter template

Let's take a look at the HTML, CSS, and Javascript code in our template and see how each piece works together. You could test this code in the SuperHi Editor, a CodePen, or the p5.js editor.

Throughout this guide, I'll introduce many built-in functions from the p5.js library, and I'll provide reference links to the p5.js documentation for each one. I encourage you to explore the p5.js documentation yourself as you follow along.

### Setting up our HTML and CSS

We'll use minimal HTML and CSS in this guide so that we can focus on painting with Javascript.

First in our `index.html`

file, we'll link the p5.js library to our project's HTML with a `<script>`

tag:

`<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>`

Then in CSS, we'll make sure our canvas fills the browser window by setting the body tag's `margin`

to 0 and hiding the scrollbars with `overflow: hidden;`

.

```
body {
margin: 0;
overflow: hidden;
}
```

### Setting up our Javascript with p5.js

Let's shift our focus to writing Javascript with the p5.js library! We'll create two key functions that drive every p5.js sketch, `setup()`

and `draw()`

, and also a third function that will act as our painting tool, `pen()`

.

From a high level, this is how our 3 Javascript functions work together:

`setup()`

sets up our painting canvas. It runs only once at the start of the program.`draw()`

is like the hand that moves the paintbrush around the canvas. It runs repeatedly.`pen()`

is the painting tool we use to paint on the canvas. It gets called via the`draw()`

function.

#### Initializing our canvas with setup()

The `setup()`

function is where we'll create our canvas and do any initialization tasks. This is also where you'd put any code that only needs to run once when the program first starts on page load.

```
function setup() {
createCanvas(windowWidth, windowHeight)
background('#fbf8f3')
}
```

Inside our setup() function, we'll initialize our canvas with two functions:

`createCanvas()`

creates a new canvas element of the specified width and height. We'll pass in the p5.js variables`windowWidth`

and`windowHeight`

so that our canvas fills the browser window.`background()`

sets the background color. Here we're using the hex color code`'#fbf8f3'`

to set the background to a light beige, but feel free to use whatever color you'd like.

#### Painting on our canvas with draw()

The second key function, `draw()`

, is where we can incorporate interactivity and animation into our p5.js sketches. It loops continuously as long as the program is running. In our case, the `draw()`

function is where we'll decide whether or not we should paint on the canvas.

```
function draw() {
if (mouseIsPressed) {
pen()
}
}
```

Inside our `draw()`

function, we'll check if the mouse button is pressed with the built-in p5.js boolean variable `mouseIsPressed`

, which returns `true`

if the mouse button is pressed or `false`

if not. If `mouseIsPressed`

is `true`

, then we'll paint on the canvas by calling our "paintbrush" function, `pen()`

.

#### Styling our paint strokes with a custom paintbrush function

Our very first "paintbrush" is the `pen()`

function, which draws a basic line:

```
function pen() {
// set the color and weight of the stroke
stroke(0, 0, 0, 255)
strokeWeight(2)
// draw a line from current mouse point to previous mouse point
line(mouseX, mouseY, pmouseX, pmouseY)
}
```

To understand how this draws to the canvas, let's break this function down step-by-step.

Inside our `pen()`

function, first we set the color of the stroke to a solid black color using the `stroke()`

function. We also set the stroke weight, which is the line thickness, to 2 pixels:

```
// set the color and weight of the stroke
stroke(0, 0, 0, 255)
strokeWeight(2)
```

We're defining our color using the RGBA color mode here, which takes in 4 numbers between 0-255 as parameters: red, green, blue, and alpha (which is the color's opacity). If you'd like, you can try out a different RGBA color, such as `stroke(33, 225, 70, 80)`

which creates a light green and slightly transparent color. To learn more about how the RGBA color mode works, along with other color modes, check out the p5.js Color guide.

Next, we draw a line with the `line()`

function, which draws a line between two points defined by their X and Y coordinates:

```
// draw a line from current mouse point to previous mouse point
line(mouseX, mouseY, pmouseX, pmouseY)
```

p5.js gives us four handy built-in variables that change with each call to `draw()`

:

`mouseX`

and`mouseY`

: the X and Y coordinates of the mouse's current location`pmouseX`

and`pmouseY`

: the X and Y coordinates of the mouse's previous location

We pass these four variables into `line()`

to create a continuous path that follows the mouse.

Putting it all together, this is the Javascript code for our starter template:

```
function setup() {
createCanvas(windowWidth, windowHeight);
background("#fbf8f3");
}
function draw() {
if (mouseIsPressed) {
pen();
}
}
function pen() {
// set the color and weight of the stroke
stroke(0, 0, 0, 255);
strokeWeight(2);
// draw a line from current mouse point to previous mouse point
line(mouseX, mouseY, pmouseX, pmouseY);
}
```

Voilà — you've just made your very first line drawing application!

For your convenience, here's the template code all in one place:

**HTML**

`<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>`

**CSS**

```
body {
margin: 0;
overflow: hidden;
}
```

**Javascript**

```
function setup() {
createCanvas(windowWidth, windowHeight);
background("#fbf8f3");
}
function draw() {
if (mouseIsPressed) {
pen();
}
}
function pen() {
// set the color and weight of the stroke
stroke(0, 0, 0, 255);
strokeWeight(2);
// draw a line from current mouse point to previous mouse point
line(mouseX, mouseY, pmouseX, pmouseY);
}
```

## Let's create more paintbrushes!

For the rest of this guide, we'll focus on creating different paintbrushes with p5.js. As you follow along, you can use this same exact template code provided above. To switch the paintbrush, simply swap `pen()`

for a different paintbrush inside of the `draw()`

function.

Also, we'll use a variety of math techniques to define our brush strokes throughout this guide. To make it easy for you to experiment with these techniques and create your own brushes, I've compiled all of these techniques as code snippets, which you'll find at the very bottom of this guide for future reference.

Now, let's explore more p5.js painting techniques and see what fun brushes emerge in the process!

## Mimicking a felt-tip marker pen

Instead of drawing a continuous pen line, let's switch things up. We'll draw a translucent circle wherever the mouse is currently located, which paints a line that feels similar to a felt-tip marker.

First, try it out for yourself:

To help you get a sense of how this paintbrush function will work before we start writing the actual code, here is a visual diagram:

Now let's start writing the code. In your Javascript file, create a new function called `marker()`

and fill it out with the steps below.

First, we'll define how we want our circles to look at the beginning of our new paintbrush function. We can set the fill color to a translucent yellow by calling the `fill()`

function with the RGBA color mode, and we can also disable the outline of the shape by calling `noStroke()`

.

```
// set the color and brush style
fill(255, 200, 103, 40)
noStroke()
```

Then, we'll draw a circle with the p5.js `circle()`

function:

```
// draw a circle at the current mouse point, with diameter of 50 pixels
circle(mouseX, mouseY, 50)
```

Similar to how we created the `pen()`

paintbrush function, we pass in `mouseX`

and `mouseY`

as parameters to draw the circle's center at the mouse's current location. To create a thick marker line, we set the circle diameter to `50`

pixels, but you can change the diameter to whatever size you'd like.

Putting it altogether, here's our `marker()`

function:

```
function marker() {
// set the color and brush style
fill(255, 200, 103, 40)
noStroke()
// draw a circle at the current mouse point, with diameter of 50 pixels
circle(mouseX, mouseY, 50)
}
```

Since you already have the template code set up, all you'd need to do to use this `marker()`

function is to call it within the `draw()`

function:

```
function draw() {
if (mouseIsPressed) {
marker()
}
}
```

## Making better brushes and painting smoother strokes

You may have noticed that your paint strokes with the `marker()`

brush don't look quite continuous. Gaps appear between the circles when you draw too quickly. Why is that?

Keep in mind that although we define how our strokes look with paintbrushes like `pen()`

and `marker()`

, it is the `draw()`

function that drives the act of painting on the canvas. Since our `marker()`

can only create one circle per call to `draw()`

, the frequency that the browser can call `draw()`

determines how smooth our `marker()`

strokes look. In this way, each call to the `draw()`

function is like a single animation frame, and repeating `draw()`

over time creates a coherent picture.

Here's another way of looking at it: the `draw()`

function is like the hand that moves our paintbrush around the canvas, but our hand can only move so fast. So, given these constraints, we have to be smart about the way we paint in order for our strokes to look good.

## Stringing circles together like beads

One way we can create more continuous paint strokes out of circles is by connecting them together in a sequence without gaps in between, resulting in what looks like a string of `beads()`

:

Each call to the `draw()`

function doesn't have to be totally independent – instead, each frame can inform the next one. We'll make each call to `draw()`

dependent on the previous call by centering the circle at the midpoint between the current and previous mouse points. The circle diameter will be the distance between the two mouse points.

To recreate this paintbrush, let's create a new Javascript function called `beads()`

and fill it out with the below steps:

First, we'll set the color of the brush and disable the stroke at the beginning of `beads()`

. Do you notice a pattern forming here? We'll always set the brush styles at the start of all of our paintbrush functions.

```
// set the color and brush style
fill(185, 83, 213, 180)
noStroke()
```

Then, we'll find the distance between the current and previous mouse points by passing their X and Y coordinates into the p5.js `dist()`

function:

```
// find the distance between the current and previous mouse points
const distance = dist(mouseX, mouseY, pmouseX, pmouseY)
```

Next, we'll find the midpoint between the current and previous mouse points:

```
// find the midpoint between the current and previous mouse points
const midX = (mouseX + pmouseX) / 2
const midY = (mouseY + pmouseY) / 2
```

And finally, we'll use these values to draw a circle with the p5.js `circle()`

function, where the circle is centered at the midpoint, and the circle's diameter is the distance:

```
// draw a circle at the midpoint, with distance as its diameter
circle(midX, midY, distance)
```

Putting it altogether, here's our final paintbrush function:

```
function beads() {
// set the color and brush style
fill(185, 83, 213, 180)
noStroke()
// find the distance between the current and previous mouse points
const distance = dist(mouseX, mouseY, pmouseX, pmouseY)
// find the midpoint between the current and previous mouse points
const midX = (mouseX + pmouseX) / 2
const midY = (mouseY + pmouseY) / 2
// draw a circle at the midpoint, with distance as its diameter
circle(midX, midY, distance)
}
```

## Shifting the colors across the rainbow

Let's switch things up a little bit. We'll use the same `beads()`

function, but instead, we'll gradually change the color in each circle to create a string of rainbow beads.

To create this rainbow effect across the sequence of circles, we'll shift the hue of each circle across the color spectrum. This would be annoying to do in the RGBA color mode, where the color is represented with 3 values for red, green, and blue. However, we can use a different color mode called HSBA, which stands for Hue, Saturation, Brightness, and Alpha. This allows us to represent the hue as a single number that ranges from 0 to 360, as illustrated here:

Let's create a new paintbrush function which we'll call `rainbowBeads()`

. Inside that function at the top, first we need to find the hue of the circle.

p5.js gives us a variable called `frameCount`

, which tells us how many frames have passed so far, or in other words, the total number of calls to `draw()`

. If we divide `frameCount`

by 360, then the remainder will always be some number from 0 to 360.

We can find the hue this way by using the modulo operator, `%`

, which returns the remainder after dividing by some number. To make the color change across the rainbow even faster, we'll multiply `frameCount`

by 10 as well.

```
// find the hue, which is a number from 0 to 360
const hue = (frameCount * 10) % 360
```

You can try experimenting with other values besides 10 and 360 as well, and see what happens.

And now that we have the hue, we can create the color in the HSBA color mode by using the p5.js `color()`

function, and then set the brush style as usual:

```
// set the color and brush style
const hsbaColor = color(`hsba(${hue}, 100%, 100%, 0.6)`)
fill(hsbaColor)
noStroke()
```

The rest of the code is the same as the `beads()`

function. Putting it all together, here's our final paintbrush function, `rainbowBeads()`

:

```
function rainbowBeads() {
// find the hue, which is a number from 0 to 360
const hue = (frameCount * 10) % 360;
// set the color and brush style
const hsbaColor = color(`hsba(${hue}, 100%, 100%, 0.6)`);
fill(hsbaColor);
noStroke();
// find the distance between the current and previous mouse points
const distance = dist(mouseX, mouseY, pmouseX, pmouseY);
// find the midpoint between the current and previous mouse points
const midX = (mouseX + pmouseX) / 2;
const midY = (mouseY + pmouseY) / 2;
// draw a circle at the midpoint, with distance as its diameter
circle(midX, midY, distance);
}
```

## Flipping between two states to paint a wiggle

Let's use the same tools we used in our `rainbowBeads()`

paintbrush to create a totally different visual effect: a wiggly line!

This wiggly line is essentially a sequence of alternating semicircles. We'll use the midpoint, distance, direction of the mouse movement, and the `frameCount`

to draw each semicircle as an arc.

First, let's create a new `wiggle()`

function and set the brush styles at the top. By default, p5.js fills in any shape with white, but we can disable this by calling `noFill()`

:

```
// set the color and brush style
stroke(255, 120, 0, 255)
strokeWeight(2)
noFill()
```

Similar to how we created the `beads()`

brush, we'll find the `distance`

between the current and previous mouse points, which will become the arc's diameter, as well as the midpoint, which will become the arc's center point.

```
// find the distance between the current and previous mouse points
const distance = dist(mouseX, mouseY, pmouseX, pmouseY)
// find the midpoint between the current and previous mouse points
const midX = (mouseX + pmouseX) / 2
const midY = (mouseY + pmouseY) / 2
```

We also want to rotate each arc towards the direction of the mouse. To figure out how much to rotate the arc, we'll calculate the `angle`

of the mouse direction with a bit of trigonometry, by finding the inverse tangent between the current and previous mouse points:

```
// find the angle of the direction the mouse is moving in
const angle = Math.atan2(mouseY - pmouseY, mouseX - pmouseX)
```

Before we draw anything, let's take a look the geometry of circles and arcs. We can define a circle as an angle of 360 degrees or 2π radians.

Since an arc is essentially a portion of a circle's circumference, we can define an arc as some angle in degrees or radians as well. A semicircle is half a circle, so it would be 180 degrees or π radians.

To define an arc in p5.js, we must choose a starting and ending angle in radians. Then, p5.js will create the arc by drawing it clockwise around a circle, starting and ending at the specified points. If we use the `angle`

of the mouse direction as the semicircle's starting angle, then since a semicircle is π radians, it would end at `angle`

plus π.

To create the wiggly line, we also need to flip each arc so that they alternate. We can flip any arc by adding π to its starting and ending angles, which will rotate the arc clockwise halfway around the circle. So, if we add π to every other arc, then we will have a wiggly line.

First, we'll figure out how we should flip the current arc by checking whether `frameCount`

is an even or odd number using `frameCount % 2`

, then multiply that by `PI.`

This would result in either 0 when `frameCount`

is an even number or `PI`

when `frameCount`

is an odd number. Adding this to the arc's starting and ending angle will flip every other arc accordingly.

```
// find which way to flip the arc
const flip = (frameCount % 2) * PI
```

And finally, with all the parameters we've gathered, we'll draw an arc with the `arc()`

function:

```
// draw the arc as a half circle
arc(midX, midY, distance, distance, angle + flip, angle + PI + flip)
```

Putting it all together, here's our final `wiggle()`

paintbrush:

```
function wiggle() {
// set the color and brush style
stroke(255, 120, 0, 255)
strokeWeight(2)
noFill()
// find the distance between the current and previous mouse points
const distance = dist(mouseX, mouseY, pmouseX, pmouseY)
// find the midpoint between the current and previous mouse points
const midX = (mouseX + pmouseX) / 2
const midY = (mouseY + pmouseY) / 2
// find the angle of the direction the mouse is moving in
const angle = Math.atan2(mouseY - pmouseY, mouseX - pmouseX)
// find which way to flip the arc
const flip = (frameCount % 2) * PI
// draw the arc as a half circle
arc(midX, midY, distance, distance, angle + flip, angle + PI + flip)
}
```

## Amplifying the tangents around the mouse path

We've drawn mostly curves and circles so far. To mix up the texture, let's explore sharper lines.

In this next paintbrush function, `toothpick()`

, we'll create toothpick-like shapes that hug the curved path of the mouse. We'll create each toothpick as an ellipse with a very large width and a very small height, and we'll rotate them towards the direction of the mouse.

As you may predict, first we'll start by setting the color and brush style at the top of the function:

```
// set the color and brush style
fill(60, 180, 0, 150)
noStroke()
```

Before we start setting up the parameters of our ellipse, let's talk about transforming shapes relative to the canvas.

In our previous paintbrush, `wiggle()`

, we drew each semicircle by rotating it in the direction of the mouse path. To draw each toothpick, instead of rotating just the shape itself, we'll first rotate the *entire canvas* around the current mouse point as the center of rotation. After transforming the whole canvas, anything we do subsequently in the `draw()`

function will be relative to this transformation.

As our first transformation, we'll set the origin of the canvas to the mouse's current location using the `translate()`

function. This means that the mouse's X and Y coordinates will become (0, 0), and the rest of the canvas coordinates will be relative to that point. By default, the origin is at the upper left corner of the browser window, unless we change it with a translation.

```
// move the origin (0,0) to the current mouse point
translate(mouseX, mouseY)
```

Then as our second transformation, we'll rotate the canvas by the angle of the mouse direction with the `rotate()`

function. The center of rotation is our mouse, since we already set the mouse location as the canvas origin.

```
// find the angle of the direction the mouse is moving in
// then rotate the canvas by that angle
const angle = Math.atan2(mouseY - pmouseY, mouseX - pmouseX)
rotate(angle)
```

To create the toothpick shape, we'll draw an ellipse with a very long width and a very short height using the `ellipse()`

function. This function takes in 4 parameters: the X and Y coordinates of its center point and its width and height.

The `ellipse()`

function takes in 4 parameters: the X and Y coordinates of its center point and its width and height.

We'll set the ellipse width based on the distance between the current and previous mouse points, and extend it a little further by multiplying by 2.

We'll also set the minimum size of the ellipse to 4 pixels by adding

`minSize`

to the ellipse width and height.Since we've already set the canvas origin and rotation based on the mouse path, we can simply draw the ellipse horizontally, where its center point's X and Y coordinates are (0, 0).

```
// set minumum width and height of the toothpick-shaped ellipse
const minSize = 4
// find the distance between current mouse point and previous mouse point
const distance = dist(mouseX, mouseY, pmouseX, pmouseY)
// draw the toothpick-shaped ellipse
ellipse(0, 0, distance * 2 + minSize, minSize)
```

Putting it all together, here's the code for our `toothpick()`

paintbrush:

```
function toothpick() {
// set the color and brush style
fill(60, 180, 0, 150)
noStroke()
// move the origin (0,0) to the current mouse point
translate(mouseX, mouseY)
// find the angle of the direction the mouse is moving in
// then rotate the canvas by that angle
const angle = Math.atan2(mouseY - pmouseY, mouseX - pmouseX)
rotate(angle)
// set minumum width and height of the toothpick-shaped ellipse
const minSize = 4
// find the distance between current mouse point and previous mouse point
const distance = dist(mouseX, mouseY, pmouseX, pmouseY)
// draw the toothpick-shaped ellipse
ellipse(0, 0, distance * 2 + minSize, minSize)
}
```

Simply by extending the width of the ellipse even further, we can create a totally different look and feel of this paintbrush function. Here, we've changed color and the ellipse's width multiplier from 2 to 18. What other variations can you create?

## Filling in the gaps between points

Another way we can smoothly connect the points along the mouse path is by repeating a point, line or shape to fill in the gaps. We can achieve this with linear interpolation – often called *lerping* in shorthand – which is a fancy math term for finding a number in between two numbers.

Let's use lerping to create a paintbrush function that mimics a fountain pen:

First, we'll create a new `fountainPen()`

function and set the brush style, as well as the `width`

of the fountain pen line.

```
// set the color and brush style
stroke(0, 0, 0, 255)
strokeWeight(1)
const width = 5
```

Technically, we could stop here and draw a slanted line right away, resulting in the following `fountainPen()`

function:

```
function fountainPen() {
// set the color and brush style
stroke(0, 0, 0, 255)
strokeWeight(1)
const width = 5
// draw a slanted line
line(mouseX - width, mouseY - width, mouseX + width, mouseY + width)
}
```

However, since the `draw()`

function can only run at a limited frequency, this function alone would leave large gaps in between each slanted line. See for yourself:

Instead of drawing a slanted line only once per call to `draw()`

, we can repeat that slanted line multiple times with the `lerp()`

function to close the gaps.

To do this, let's create a variable called `lerps`

, which will become the number of times we repeat a `for`

loop:

```
// set the number of times we repeat the line
const lerps = 16
// repeat the slanted line with lerping
for (let i = 0; i < lerps; i++) {
}
```

Next, we'll use the `for`

loop to repeatedly draw the slanted line at regular intervals between the current and previous mouse points.

Inside the for loop, first we need to figure out where to draw each line by finding its lerped `x`

and `y`

coordinates with the `lerp()`

function. Given any two numbers and some ratio between 0.0 to 1.0, `lerp()`

will return a number between the 2 numbers at the given ratio.

To find the

`x`

coordinate, we'll lerp between`mouseX`

and`pmouseX`

, and to find the`y`

coordinate, we'll lerp between`mouseY`

and`pmouseY`

.

To create even spacing between each line, we'll divide the loop index

`i`

by`lerps`

, the total number of loop iterations, to get the lerping ratio`i / lerps`

.

This is the resulting code for our `for`

loop where we repeatedly draw the slanted line:

```
// set how many times to lerp the line
const lerps = 16
// repeat the slanted line with lerping
for (let i = 0; i < lerps; i++) {
const x = lerp(mouseX, pmouseX, i / lerps)
const y = lerp(mouseY, pmouseY, i / lerps)
line(x - width, y - width, x + width, y + width)
}
```

Finally, putting it all together, here's the code for our `fountainPen()`

paintbrush:

```
function fountainPen() {
// set the color and brush style
stroke(0, 0, 0, 255)
strokeWeight(1)
const width = 5
// set the number of times we lerp the line in the for loop
const lerps = 16
// repeat the slanted line with lerping
for (let i = 0; i <= lerps - 1; i++) {
// find the lerped x and y coordinates between the mouse points
const x = lerp(mouseX, pmouseX, i / lerps)
const y = lerp(mouseY, pmouseY, i / lerps)
// draw a slanted line
line(x - width, y - width, x + width, y + width)
}
}
```

## Lerping points to create a splatter effect

The fun thing about creative coding is that sometimes silly mistakes turn into spontaneous surprises! While creating the `fountainPen()`

paintbrush, I accidentally made a mistake with the ratio in the `lerp()`

function. Lo and behold, this `splatter()`

paintbrush was born:

The `splatter()`

and `fountainPen()`

functions are quite similar in code. So instead of walking you through how to code the `splatter()`

paintbrush function step-by-step, I'll briefly outline the key differences:

The third parameter to

`lerp()`

, which is the lerp ratio, is`i / lerps + lerps`

. This not only spaces each lerped point apart from each other at regular intervals with`i / lerps`

, but adding`lerps`

also slightly shifts the point by the number of pixels.Instead of drawing a line, we draw a point at the lerped X and Y coordinates with the

`point()`

function.We also use a few minor stylistic differences in color, stroke weight and number of loop iterations in the

`lerps`

variable (8`lerps`

instead of 16).

Putting it all together, here is the code for the `splatter()`

paintbrush function:

```
function splatter() {
// set the color and brush style
stroke(frameCount % 255, 180, 255, 160)
strokeWeight(4)
// set the number of times we lerp the point in the for loop
const lerps = 8
// repeat the point with lerping
for (let i = 0; i < lerps; i++) {
// find lerped x and y coordinates of the point
const x = lerp(mouseX, pmouseX, i / lerps + lerps)
const y = lerp(mouseY, pmouseY, i / lerps + lerps)
// draw a point
point(x, y)
}
}
```

## Hatching

Let's explore this lerping technique even further. Instead of repeating the same slanted line as in the `fountainPen()`

brush, how can we create sketchier and messier strokes? This next paintbrush function echoes a traditional sketching technique called hatching, which is used to darken areas of an illustration by drawing parallel lines.

Let's create a new paintbrush function called `hatching()`

. At the top, we'll set the brush style and also calculate the `speed`

of the mouse movement.

```
// set the color and brush style
stroke(15, 15, 255, 220)
strokeWeight(2)
// calculate the speed of the mouse
let speed = abs(mouseX - pmouseX) + abs(mouseY - pmouseY)
```

So far in this guide, we've drawn shapes and lines based on the relationship between the current mouse point (`mouseX`

and `mouseY`

) and the previous mouse point (`pmouseX`

and `pmouseY`

). One way we can more easily manipulate this relationship is with a *vector*, which is an object that has both a direction and magnitude. You can think of a vector as an arrow of some length that points towards some direction.

To create a vector, we can pass in an `X`

value and a `Y`

value into the `createVector()`

function. This will create a p5.Vector object with the following properties:

The vector direction is the direction from the origin (0, 0) to the point at

`X`

and`Y`

. In p5.js, this is an angle in radians.The vector magnitude is the distance between the origin and the point at

`X`

and`Y`

.

For example, if we create a vector with the current mouse point and another vector with the previous mouse point, the below diagram shows how we can visualize them:

However, instead of creating vectors out of the current and previous mouse points, let's combine them: we'll find the `Y`

value by subtracting `pmouseX`

from `mouseX`

, and the `X`

value by subtracting `pmouseY`

from `mouseY`

.

```
// make a vector by inverting X and Y values
const vector = createVector(mouseY - pmouseY, mouseX - pmouseX)
```

Swapping the `X`

and `Y`

values inverts the direction of our resulting vector. To get a better sense of how this swap results in each line of the brush stroke, try hovering your mouse over the interactive diagram below and observe how the orientation of the line changes:

Next, we'll set the vector magnitude to depend on the `speed`

of the mouse with the `setMag()`

vector function, which is one of many p5.Vector methods that can be used to manipulate vectors. This determines the length of our line.

```
// set the vector magnitude (the line length) based on the mouse speed
vector.setMag(speed / 2)
```

Technically, this entire `hatching()`

function could be created without using any vectors. However, having access to vector functions like `setMag()`

enables us to quickly and conveniently manipulate the relationships between points, lines, and shapes like this with very little code, which spares us from doing the extra math ourselves. You can check out the p5.Vector object documentation to see the full list of p5.js vector functions.

And finally, just as we lerped the lines of the `fountainPen()`

paintbrush, we'll do the same with `hatching()`

to make our brush strokes smoother and denser:

```
// set the number of times we lerp the line
const lerps = 3
// repeat the line with lerping
for (let i = 0; i < lerps; i++) {
// find the lerped X and Y coordinates
const x = lerp(mouseX, pmouseX, i / lerps)
const y = lerp(mouseY, pmouseY, i / lerps)
// draw a line
line(x - vector.x, y - vector.y, x + vector.x, y + vector.y)
}
```

Putting it all together, here's the code for our `hatching()`

paintbrush:

```
function hatching() {
// set the color and brush style
stroke(15, 15, 255, 220)
strokeWeight(1)
// calculate the speed of the mouse
let speed = abs(mouseX - pmouseX) + abs(mouseY - pmouseY)
// make a vector by inverting X and Y values
const vector = createVector(mouseY - pmouseY, mouseX - pmouseX)
// set the vector magnitude (the line length) based on the mouse speed
vector.setMag(speed / 2)
// set the number of times we lerp the line
const lerps = 3
// repeat the line with lerping
for (let i = 0; i < lerps; i++) {
// find the lerped X and Y coordinates
const x = lerp(mouseX, pmouseX, i / lerps)
const y = lerp(mouseY, pmouseY, i / lerps)
// draw a line
line(x - vector.x, y - vector.y, x + vector.x, y + vector.y)
}
}
```

## Spray painting with randomness

As our final paintbrush in this guide, we'll mimic the look and feel of painting with a can of `sprayPaint()`

, and introduce randomness into the process. Before we start building it, try it out yourself:

First, we'll create a new `sprayPaint()`

function. Then at the top, we'll set the brush styles:

```
// set the color and brush style
stroke(0, 0, 0, 255)
strokeWeight(1)
```

Each call to this paintbrush function will draw a bunch of random points within a circle, which will get bigger as the mouse moves faster.

To define the dimensions, we'll set up some initial variables:

`minRadius`

as the minimum radius of the brush stroke, even when the mouse speed is 0`sprayDensity`

as the number of random points to draw within the circle`speed`

of the mouse`r`

as the radius of the circle, which is the`minRadius`

at its smallest size, and increases with the`speed`

of the mouse`rSquared`

, which we'll later use to calculate the positions of the random points

```
// set minimum radius and spray density of spray paint brush
const minRadius = 10
const sprayDensity = 80
// find the speed of the mouse movement
const speed = abs(mouseX - pmouseX) + abs(mouseY - pmouseY)
// find the radius of the spray paint brush, and also the radius squared
const r = speed + minRadius
const rSquared = r * r
```

We can draw the random points by first creating a `for`

loop, which runs as many times as the `sprayDensity`

. You can create darker, denser paint strokes by increasing the `sprayDensity`

.

```
// draw random points repeatedly within the circle with radius r
for (let j = 0; j < sprayDensity; j++) {
}
```

Each time we run through this `for`

loop, we'll draw a random point within the circle by using the `random()`

function. The `random()`

function takes in two numbers – a minimum and maximum number – and returns a random number within that range.

In order to make sure that each random point falls inside the circle, we'll need to use a bit of math. So, let's break down the geometry of circles!

We can define any circle with the equation `r^2 = x^2 + y^2`

, where `r`

is the radius, and `x`

and `y`

are any point on the circumference of the circle. In case this equation looks familiar to you, this circle formula is based on the Pythagorean theorem. If we isolate the `y`

variable on one side of the equals sign, we get `y = sqrt(r^2 - x^2)`

.

So, let's say we have a circle with some radius `r`

. Our goal is to find the `x`

and `y`

coordinates of some random point within that circle.

If we choose any

`x`

that is between`-r`

and`r`

, then plugging those`x`

and`r`

values into the circle equation will give us some`y`

that equals`sqrt(r^2 - x^2)`

. These two`x`

and`y`

coordinates together would make a point that sits on the circumference of the circle.Then, as long as we choose some

`x`

that is between`-r`

and`r`

, and also some`y`

that is between`-sqrt(r^2 - x^2)`

and`sqrt(r^2 - x^2)`

, then the random point will fall somewhere*inside*the circle.

Using these rules, we can now use the `random()`

function to draw a bunch of random points within the circle:

```
// draw random points within a circle
for (let j = 0; j < sprayDensity; j++) {
// pick a random position within the circle
const randX = random(-r, r)
const randY = random(-1, 1) * sqrt(rSquared - randX * randX)
// draw the random point
point(mouseX + randX, mouseY + randY)
}
```

As the final step, to create even denser and smoother spray paint strokes, we'll add lerping. We can do this by nesting our first `for`

loop inside of another `for`

loop.

In the

*outer*`for`

loop, we'll calculate the lerped`X`

and`Y`

coordinates with the`lerp()`

function, in the same way we used lerping to create the previous three paintbrush functions.In the

*inner*`for`

loop, we'll draw the random points by combining the lerped`X`

and`Y`

with the random`X`

and`Y`

coordinates.

```
// set the number of times we lerp the points in the for loop
const lerps = 10
// repeat the random points with lerping
for (let i = 0; i < lerps; i++) {
// find the lerped X and Y coordinates
const lerpX = lerp(mouseX, pmouseX, i / lerps)
const lerpY = lerp(mouseY, pmouseY, i / lerps)
// draw random points within a circle
for (let j = 0; j < sprayDensity; j++) {
// pick a random position within the circle
const randX = random(-r, r)
const randY = random(-1, 1) * sqrt(rSquared - randX * randX)
// draw the random point
point(lerpX + randX, lerpY + randY)
}
}
```

And finally, putting it all together, here's our `sprayPaint()`

function:

```
function sprayPaint() {
// set the color and brush style
stroke(0, 0, 0, 255)
strokeWeight(1)
// find the speed of the mouse movement
const speed = abs(mouseX - pmouseX) + abs(mouseY - pmouseY)
// set minimum radius and spray density of spraypaint brush
const minRadius = 10
const sprayDensity = 80
// find radius of the spray paint brush and radius squared
const r = speed + minRadius
const rSquared = r * r
// set the number of times we lerp the points in the for loop
const lerps = 10
// repeat the random points with lerping
for (let i = 0; i < lerps; i++) {
// find the lerped X and Y coordinates
const lerpX = lerp(mouseX, pmouseX, i / lerps)
const lerpY = lerp(mouseY, pmouseY, i / lerps)
// draw a bunch of random points within a circle
for (let j = 0; j < sprayDensity; j++) {
// pick a random position within the circle
const randX = random(-r, r)
const randY = random(-1, 1) * sqrt(rSquared - randX * randX)
// draw the random point
point(lerpX + randX, lerpY + randY)
}
}
}
```

## Conclusion

At this point, you've created 10 distinct paintbrushes! But this is just a starting point – there are endless ways you can mix and match the techniques used throughout this guide to create your own unique painting tools.

As you keep going, do explore the p5.js library documentation and see the breadth of p5.js functions and creative coding tools you have at your disposal. I'll also include some links to more p5.js learning resources below, as well as a compilation of the mathy code snippets that we used throughout this guide for your future reference.

In the next guide of this Painting with Code series, we'll combine all of these paintbrushes into a single painting application, so that you can paint with all of your brushes on a single canvas. And in the process, we'll learn how to prototype user interfaces for painting applications with p5.js, so that you can customize the entire painting experience within the browser.

Have fun with it and we'd love to see what you create!

## Additional learning resources

Here is a list of all of the p5.js functions and variables used throughout this guide.

The p5.js website has several useful guides and step-by-step tutorials for learning to use p5.js. It also has a great p5.js reference page for all the p5.js functions used here in this guide.

The Coding Train has a huge trove of p5.js tutorials, and is run by Daniel Shiffman, who is dedicated to creating accessible and beginner-friendly learning resources for creative coding.

If you'd like to learn more advanced drawing and interaction techniques with p5.js, check out SuperHi's Math for Creatives and Experimental Typography for the Web courses.

## Code snippets

Throughout this guide, we used a variety of mini math techniques to stylize our paintbrushes. To make it easy for you to experiment with these techniques and create your own brushes, this is a compilation of these code snippets for your future reference.

#### Speed of the mouse movement

```
// find the speed of the mouse movement
let speed = abs(mouseX - pmouseX) + abs(mouseY - pmouseY)
```

#### Distance between current and previous mouse points

```
// find the distance between the current and previous mouse points
const distance = dist(mouseX, mouseY, pmouseX, pmouseY)
```

#### Midpoint between current and previous mouse points

```
// find the midpoint between the current and previous mouse points
const midX = (mouseX + pmouseX) / 2
const midY = (mouseY + pmouseY) / 2
```

#### Angle of mouse direction

```
// find the angle of the direction the mouse is moving in
const angle = Math.atan2(mouseY-pmouseY, mouseX-pmouseX)
```

#### Looping between x number of states in each call to draw()

`const state = frameCount % x`

#### Filling in the gaps with lerp() inside a for loop

```
// set the number of times we lerp the point
const lerps = 16
// repeat the point with lerping
for (let i = 0; i <= lerps; i++) {
// find x and y coordinates of the point
const x = lerp(mouseX, pmouseX, i / lerps)
const y = lerp(mouseY, pmouseY, i / lerps)
// draw the point
point(x - width, y - width, x + width, y + width)
}
```

#### Picking a random position inside of a circle with radius r

```
// set radius of the circle and calculate the radius squared
const r = 10
const rSquared = r * r
// pick a random position within the circle
const randX = random(-r, r)
const randY = random(-1, 1) * sqrt(rSquared - randX * randX)
```

Arianna Ninh is an Educator at SuperHi, where she supports students in embracing the messy magic of learning something new. She hails from the San Francisco Bay Area and loves hiking outdoors in the SF fog (aka Karl). Outside of SuperHi, you can find her attempting to make food or weird sculptures, or daydreaming in a park somewhere.