🚀 Check out my $1,000,000 journey!

Drawing App built with p5js

7 min read

Drawing App with p5js

The theme for week #5 of the Weekly Coding Challenge is:

Drawing Application

This is the first application that we are building in the #weeklyCodingChallenge program. So far we have built smaller projects, so this is pretty exciting if you ask me! 😄

In this article we’ll use p5js, a drawing library, to build a Drawing Application:

https://codepen.io/FlorinPop17/pen/VNYyZQ

If you want to learn more about p5js and what it does visit their official website. Basically I am using it because it works very well on top of the browser’s canvas element by providing a clear API.

The HTML

As you can notice in the example above, in the left side of the screen we have a .sidebar. We’ll put inside it our ‘tools’ - a color picker, a weight selector and the clear button (trashcan icon):

<div class="sidebar">
    <ul>
        <li>
            <label for="color">Color:</label>
            <input type="color" id="color" />
        </li>
        <li>
            <label for="weight">Stroke:</label>
            <input type="number" id="weight" min="2" max="200" value="3" />
        </li>
        <li>
            <button id="clear"><i class="fa fa-trash"></i></button>
        </li>
    </ul>
</div>

The CSS

Using CSS we’ll move the .sidebar and everything that’s inside it in the left side and style it a little bit to make it look nicer (nothing really fancy, just basic CSS):

.sidebar {
    background-color: #333;
    box-shadow: 0px 0px 10px rgba(30, 30, 30, 0.7);
    color: #fff;
    position: absolute;
    left: 0;
    top: 0;
    height: 100vh;
    padding: 5px;
    z-index: 1000;
}

.sidebar ul {
    display: flex;
    justify-content: center;
    align-items: flex-start;
    flex-direction: column;
    list-style-type: none;
    padding: 0;
    margin: 0;
    height: 100%;
}

.sidebar ul li {
    padding: 5px 0;
}

.sidebar input,
.sidebar button {
    text-align: center;
    width: 45px;
}

.sidebar li:last-of-type {
    margin-top: auto;
}

.sidebar button {
    background-color: transparent;
    border: none;
    color: #fff;
    font-size: 20px;
}

.sidebar label {
    display: block;
    font-size: 12px;
    margin-bottom: 3px;
}

Now for the important part…

The JS / P5JS

As you might have noticed, we haven’t added a canvas element into our HTML since p5js will create it for us.

There are two important functions which we’ll use from the p5js library:

  • setup - is called once when the program starts. It’s used to define initial environment properties such as screen size and background color…
  • draw - called directly after setup(), the draw() function continuously executes the lines of code contained inside its block
function setup() {
    // create a canvas which is full width and height
    createCanvas(window.innerWidth, window.innerHeight);

    // Add a white background to the canvas
    background(255);
}

function draw() {}

Before moving forward, let’s stop for a moment and see what we want to achieve.

So, basically we want to add a mousepressed eventListener to the canvas that will start ‘drawing’ a shape inside it as long as the mouseIsPressed.

We’ll create an array of points which we’re going to use to create a path (or a shape) using the beginShape and endShape methods to draw this shape inside the canvas. The shape is going to be constructed by connecting a series of vertices (see vertex for more information).

As we want this shape to be re-drawn every time, we’ll put this code inside the draw method:

const path = [];

function draw() {
    // disabled filling geometry - p5js function
    noFill();

    if (mouseIsPressed) {
        // Store the location of the mouse
        const point = {
            x: mouseX,
            y: mouseY
        };
        path.push(point);
    }

    beginShape();
    path.forEach(point => {
        // create a vertex at the specified location
        vertex(point.x, point.y);
    });
    endShape();
}

As you can see, p5js has a mouseIsPressed flag that we can use to detect when the mouse buttons is pressed.

Everything might look good so far, but there is a big issue. Once the mouse button is released and we try to draw another shape, the last point from the previous shape will be connected to the first point of the new shape, which is definitely not what we want, so… We need to change our approach a little bit.

Instead of having one array of points (the path array), we’ll create a paths array and we are going to store all the paths inside it. Basically we’ll have a double array with points. Also, for this we will to need to keep track of the currentPath while the mouse is still pressed and we’ll reset this array once the mouse button is pressed again. Confusing? 🤔 Let’s see the code and I bet that it will become more clear:

const paths = [];
let currentPath = [];

function draw() {
    noFill();

    if (mouseIsPressed) {
        const point = {
            x: mouseX,
            y: mouseY
        };
        // Adding the point to the `currentPath` array
        currentPath.push(point);
    }

    // Looping over all the paths and drawing all the points inside them
    paths.forEach(path => {
        beginShape();
        path.forEach(point => {
            stroke(point.color);
            strokeWeight(point.weight);
            vertex(point.x, point.y);
        });
        endShape();
    });
}

// When the mouse is pressed, this even will fire
function mousePressed() {
    // Clean up the currentPath
    currentPath = [];

    // Push the path inside the `paths` array
    paths.push(currentPath);
}

I also added some comments in the code above, make sure you check them out.

The mousePressed function is called once after every time a mouse button is pressed - p5js stuff! 😛

Great! Now we can draw individual shapes in our canvas! 😄

The last thing to do is to hook up those buttons that we created in the HTML and use the values that are inside them to style the shape:

const colorInput = document.getElementById('color');
const weight = document.getElementById('weight');
const clear = document.getElementById('clear');

function draw() {
    noFill();

    if (mouseIsPressed) {
        const point = {
            x: mouseX,
            y: mouseY,
            // storing the color and weights provided by the inputs for each point
            color: colorInput.value,
            weight: weight.value
        };
        currentPath.push(point);
    }

    paths.forEach(path => {
        beginShape();
        path.forEach(point => {
            // using the color and the weight to style the stroke
            stroke(point.color);
            strokeWeight(point.weight);
            vertex(point.x, point.y);
        });
        endShape();
    });
}

clear.addEventListener('click', () => {
    // Remove all the paths
    paths.splice(0);

    // Clear the background
    background(255);
});

And with this we have finished our little application! Yey! 😃

The entire JS code

const colorInput = document.getElementById('color');
const weight = document.getElementById('weight');
const clear = document.getElementById('clear');
const paths = [];
let currentPath = [];

function setup() {
    createCanvas(window.innerWidth, window.innerHeight);
    background(255);
}

function draw() {
    noFill();

    if (mouseIsPressed) {
        const point = {
            x: mouseX,
            y: mouseY,
            color: colorInput.value,
            weight: weight.value
        };
        currentPath.push(point);
    }

    paths.forEach(path => {
        beginShape();
        path.forEach(point => {
            stroke(point.color);
            strokeWeight(point.weight);
            vertex(point.x, point.y);
        });
        endShape();
    });
}

function mousePressed() {
    currentPath = [];
    paths.push(currentPath);
}

clear.addEventListener('click', () => {
    paths.splice(0);
    background(255);
});

Also make sure that you import the p5js file in your html too before importing this js file.

Conclusion

I hope that you liked this drawing app that we’ve built. There are a bunch of functionalities that could be added to this app and I challenge you to let your creative mind to come up with new ideas! 🙂

What if you could save the drawing as an image (.png or .jpg)? 🤔 (you can do this with the p5js library).

As of now we are only checking the mouse events, but maybe you could make it work on mobile too by figuring out the touch events? The sky is the limit with the amount of functionalities that could be added to this app!

I’d love to see what you are going to build! Tweet me @florinpop1705 with your creation! :+1:

You might also like one of the other challenges from the Weekly Coding Challenge program. Check them out here.

See ya next time! Happy Coding! 😇

    Want to stay updated?
    Sign up for the newsletter.

    No spam. Unsubscribe at any time.