Pixelart tool using p5js
Tags: javascript, programming, webdev, tutorial
Published: 2021-12-09 06:59:38

Hello, My name's Amirhossein Veysi and today i want to teach you how to make a pixelart tool using [p5js](https://p5js.org/) a JavaScript library for creative coding. If something is wrong or could be better in this blog, let me know by leaving a comment, Thanks.

## Introduction
The tool will have a artboard size customization, Also includes brush, eraser, custom brush color and custom background color as well.

## How to use
To make up p5js you just need to setup its CDN from [here](https://cdnjs.com/libraries/p5.js), or if you want to save it locally you can download it from [here](https://p5js.org/download/).

## Let's start
After you setup p5js, it's time to start working on the pixelart tool, so we start with basic HTML like this:

```HTML
    <!--Container-->
<div class="container">
  <!--Header-->
  <header>
    <a class="brand" href="#">Pixelart Tool</a>
  </header>
  <!--Sidebar-->
  <div class="sidebar">
    <div class="controls">
      <div class="control selected" data-tool="brush">Brush</div>
      <div class="control" data-tool="eraser">Eraser</div>
      <div class="control">
        <label for="brush-color">Brush Color</label>
        <input id="brush-color" type="color" />
      </div>
      <div class="control">
        <label for="board-color">Board Color</label>
        <input id="board-color" type="color" />
      </div>
      <div id="download-btn" class="control">Download</div>
    </div>
  </div>
</div>
<!--Popup form-->
<div class="popup-container">
  <form class="popup">
    <p class="head">Please Specify Your Artboard</p>
    <div class="setting">
      <label for="board-width">BOARD WIDTH</label>
      <input id="board-width" type="number" min="1" />
    </div>
    <div class="setting">
      <label for="board-height">BOARD HEIGHT</label>
      <input id="board-height" type="number" min="1" />
    </div>
    <button type="button">Continue</button>
  </form>
</div>
```

## Make it look nicer
Then we want our app to look nicer, So we'll add styles to it, you can use the following styles, Also you can use your own.

```css
header {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 60px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: #f1f1f1;
  z-index: 999;
}

header .brand {
  font-size: 20px;
  text-decoration: none;
  margin-left: 20px;
  font-family: cursive;
  color: #888;
  max-width: 119px;
  transition: 0.3s;
}

header .brand:hover {
  color: #444;
}

.sidebar {
  position: fixed;
  left: 0;
  top: 0;
  padding-top: 60px;
  width: 80px;
  height: 100%;
  background: rgb(207, 207, 207);
  display: flex;
  justify-content: center;
}

.controls .control {
  width: 65px;
  background: #fcfcfc;
  color: #666;
  padding: 10px 5px;
  border-radius: 5px;
  cursor: pointer;
  text-align: center;
  margin-top: 15px;
  font-size: 13px;
  user-select: none;
  transition: 0.3s;
}

.controls .control.selected {
  background: #ccc;
}

.controls .control input {
  width: 90%;
  margin-top: 7px;
}

.controls .control:hover {
  background: #eee;
}

.controls .control.active {
  background: #cecece;
}

.popup-container {
  position: fixed;
  left: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  background: rgba(255, 255, 255, 0.5);
  backdrop-filter: blur(2px);
  z-index: 1030;
}

.popup {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 90%;
  max-width: 600px;
  background: #fff;
  padding: 1.25rem;
  border-radius: 0.5rem;
  box-shadow: 0 0 15px #ccc;
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
}

.popup .head {
  font-size: 25px;
  color: #666;
  text-align: center;
  width: 100%;
}

.popup .setting {
  margin-top: 15px;
  width: 100%;
}

.popup label {
  font-size: 14px;
}

.popup input {
  width: 100%;
  height: 45px;
  background: #f6f6f6;
  border-radius: 0.25rem;
  outline: 0;
  border: 0;
  padding: 15px;
  font-size: 16px;
  margin-top: 5px;
  transition: 0.3s;
}

.popup button {
  width: 50%;
  height: 45px;
  background: #f6f6f6;
  border: 0;
  outline: 0;
  border-radius: 0.25rem;
  margin-top: 20px;
  color: #666;
  font-size: 18px;
  cursor: pointer;
}

.popup input:focus,
.popup button:hover {
  background: #f1f1f1;
}

main canvas{
    cursor: url(../img/brush.svg), pointer;
}
```

## P5js Power
So now we're going to make the pixelart work fine using p5js, I'll describe the map step by step.

## Basics
Let's make the basic things like, the canvas, grid and etc.

### Required functions
We need two functions to get started, more info in the [documentation](https://p5js.org/get-started/)
```js
function setup() {
 // setup code here
}

function draw(){
// drawing code here
}
```

### Create canvas
To draw a pixelart, we need a canvas, here's how to create one:
```js
let canvas = createCanvas(); // We'll resize it later using popup form
```

### Select Elements
If we want to work with html elements, we need to select them, so:
```js
const artBoardWidthInp = select("#board-width");
const artBoardHeightInp = select("#board-height");
const brushColorInp = select("#brush-color");
const boardColorInp = select("#board-color");
const popUpBtn = select(".popup button");
const downloadBtn = select("#download-btn");
const controls = selectAll(".control[data-tool]");
```

### Grid variable
To hold the grid, we need top level variables, so we define them out of any function:
```js
let grid = null; // Null by default
let cols = 0;
let rows = 0;
```

### Pixel object
Let's make a pixel object with its properties:
```js
function Pixel(x, y) {
  this.color = boardColor;
  this.colored = false;

  this.show = function () {
    fill(color(this.color));
    stroke(0);
    rect(x * boxSize, y * boxSize, boxSize, boxSize);
  };
}
```

### Popup button click handler
We need to make the artboard using the values in popup form, so:
```js
popUpBtn.mouseClicked(() => {
  resizeCanvas(artBoardWidthInp.value(), 
  artBoardHeightInp.value()); // Resize the canvas

  select(".popup-container").style("display", "none");
  cols = Math.floor(width / boxSize); // Calculate columns
  rows = Math.floor(height / boxSize); // Calculate rows
  grid = new Array(cols); // Assign an array with the length of columns to the grid

  for (let i = 0; i < cols; i++) {
    grid[i] = new Array(rows); // Push an array with the length of rows to each column
  }

  for (let y = 0; y < cols; y++) { // Loop over columns
    for (let x = 0; x < rows; x++) { // Loop over Rows
      grid[y][x] = new Pixel(y, x); // Add a pixel to the each axis
    }
  }
});
```

## Advanced tools
let's get into the field of tools.

### Brush and artboard color
The brush and artboard color will be in top level variables: 
```js
let brushColor = 000; // Black by default
let boardColor = 255; // White by default
```

### Change colors tools
We have tools in the html, however they're not working yet, let's make them:
```js
brushColorInp.changed(() => {
  brushColor = brushColorInp.value(); // Changes the board color
});

boardColorInp.changed(() => {
  boardColor = boardColorInp.value(); // Changes the artboard color
});

downloadBtn.mouseClicked(() => {
  saveCanvas(canvas, 'pixel-art', 'jpg'); // Downloads the art
});
```

### Current selected tool
We store the selected tool in a top level variable as well as  other tools.
```js
let tool = "brush"; // Default tool is brush
```

### Change selected tool
We change the selected tool each time one of them has been clicked.
```js
controls.forEach((el) => {
  el.mouseClicked((event) => {
    controls.forEach((el) => el.removeClass("selected")); // Remove "selected" clas from all elements
    event.target.classList.add("selected"); // Add "selected" class to the clicked element
    tool = event.target.dataset.tool; // Assign the clicked tool
  });
});
```

### Is mouse pressed?
We need to check whether the mouse is pressed or not, we'll use it later, So let's start with a top level variable:

```js
let pressed = false;
```

We change the value of this variable using [`mousePressed`](https://p5js.org/reference/#/p5/mousePressed) & [`mouseReleased`](https://p5js.org/reference/#/p5/mouseReleased):

```js
function mousePressed() {
  pressed = true;
}

function mouseReleased() {
  pressed = false;
}
```

## Draw things
It's time to start drawing the stuff that we just created till now. We'll draw them all using the `draw` function.

### The grid
Let's start drawing by Looping throw the grid:

```js
for (let y = 0; y < cols; y++) {
    for (let x = 0; x < rows; x++) {
      // code goes here
    }
}
```

### Draw the grid pixels
Let's draw the grid pixel, using the following code inside of the loop:

```js
grid[y][x].show();
```

### Check whether the mouse is over `grid[y][x]`
We need to know if the mouse is over the current pixel, so add the following statement inside of the loop:

```js
if (
      mouseY > x * boxSize &&
      mouseY < x * boxSize + boxSize &&
      mouseX > y * boxSize &&
      mouseX < y * boxSize + boxSize
    ) {
      // mouse is over it
    } else {
      // mouse is not over it
    }
```

### Paint the pixel
To paint the pixel we need to check if mouse is clicked by the `pressed` variable, add the following code where the pixel is hovered:

```js
if (pressed) {
 grid[y][x].colored = tool == "brush";
 grid[y][x].color = tool == "brush" ? selectedColor : boardColor;
}
```

### Pixel hover preview
If you want the pixel color to change while mouse is over it, And then make it back to it's default color whenever the mouse leaves it (and the user didn't press the mouse), define a top level variable:

```js
let temporaryColor = null;
```

Then add the following code where the mouse is pressed to store the current color of colored pixel:

```js
if (grid[y][x].colored) {
  temporaryColor = { x, y, color: grid[y][x].color };
}
```

Finally just add:

```js
grid[y][x].color = tool == "brush" ? selectedColor : boardColor;
```

### While pixel is not hovered
We should clear the preview color when the pixel is not hovered.
#### First step
The first step is to change the color of the painted pixel back to it's previous color which is stored in `temporaryColor`, so add the following code where the pixel is not hovered:

```js
if (temporaryColor) {
  grid[temporaryColor.y][temporaryColor.x].color = temporaryColor.color;
  temporaryColor = null;
}
```

#### Second step
the second step is to change the color of the unpainted pixel, add this code where the pixel is not hovered:

```js
if (!grid[y][x].colored) {
  grid[y][x].color = boardColor;
}
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqalpwetyfkh8qmi6fik.png)

Hopefully the tutorial above helped you to know how to create a pixelart tool. If you have anything to say, feel free to leave a comment.

If you learned something from this tutorial, please hit the like button.

#### Source code [here](https://github.com/Amirhossein-Veysi/pixel-art-tool)
#### Live example [here](http://pixel-art-tool.vercel.app/)