DEV Community

Cover image for Pixelart tool using p5js
Amirhossein Veysi
Amirhossein Veysi

Posted on

Pixelart tool using p5js

Hello, My name's Amirhossein Veysi and today i want to teach you how to make a pixelart tool using p5js 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, or if you want to save it locally you can download it from here.

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:

    <!--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>
Enter fullscreen mode Exit fullscreen mode

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.

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;
}
Enter fullscreen mode Exit fullscreen mode

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

function setup() {
 // setup code here
}

function draw(){
// drawing code here
}
Enter fullscreen mode Exit fullscreen mode

Create canvas

To draw a pixelart, we need a canvas, here's how to create one:

let canvas = createCanvas(); // We'll resize it later using popup form
Enter fullscreen mode Exit fullscreen mode

Select Elements

If we want to work with html elements, we need to select them, so:

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]");
Enter fullscreen mode Exit fullscreen mode

Grid variable

To hold the grid, we need top level variables, so we define them out of any function:

let grid = null; // Null by default
let cols = 0;
let rows = 0;
Enter fullscreen mode Exit fullscreen mode

Pixel object

Let's make a pixel object with its properties:

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);
  };
}
Enter fullscreen mode Exit fullscreen mode

Popup button click handler

We need to make the artboard using the values in popup form, so:

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
    }
  }
});
Enter fullscreen mode Exit fullscreen mode

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:

let brushColor = 000; // Black by default
let boardColor = 255; // White by default
Enter fullscreen mode Exit fullscreen mode

Change colors tools

We have tools in the html, however they're not working yet, let's make them:

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
});
Enter fullscreen mode Exit fullscreen mode

Current selected tool

We store the selected tool in a top level variable as well as other tools.

let tool = "brush"; // Default tool is brush
Enter fullscreen mode Exit fullscreen mode

Change selected tool

We change the selected tool each time one of them has been clicked.

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
  });
});
Enter fullscreen mode Exit fullscreen mode

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:

let pressed = false;
Enter fullscreen mode Exit fullscreen mode

We change the value of this variable using mousePressed & mouseReleased:

function mousePressed() {
  pressed = true;
}

function mouseReleased() {
  pressed = false;
}
Enter fullscreen mode Exit fullscreen mode

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:

for (let y = 0; y < cols; y++) {
    for (let x = 0; x < rows; x++) {
      // code goes here
    }
}
Enter fullscreen mode Exit fullscreen mode

Draw the grid pixels

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

grid[y][x].show();
Enter fullscreen mode Exit fullscreen mode

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:

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
    }
Enter fullscreen mode Exit fullscreen mode

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:

if (pressed) {
 grid[y][x].colored = tool == "brush";
 grid[y][x].color = tool == "brush" ? selectedColor : boardColor;
}
Enter fullscreen mode Exit fullscreen mode

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:

let temporaryColor = null;
Enter fullscreen mode Exit fullscreen mode

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

if (grid[y][x].colored) {
  temporaryColor = { x, y, color: grid[y][x].color };
}
Enter fullscreen mode Exit fullscreen mode

Finally just add:

grid[y][x].color = tool == "brush" ? selectedColor : boardColor;
Enter fullscreen mode Exit fullscreen mode

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:

if (temporaryColor) {
  grid[temporaryColor.y][temporaryColor.x].color = temporaryColor.color;
  temporaryColor = null;
}
Enter fullscreen mode Exit fullscreen mode

Second step

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

if (!grid[y][x].colored) {
  grid[y][x].color = boardColor;
}
Enter fullscreen mode Exit fullscreen mode

Image description

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

Live example here

Top comments (3)

Collapse
 
luisgregson profile image
Luis Gregson

nice tutorial with good links to resources. I think you forgot to mention that you need to declare boxSize variable too (it is present when you look in the source code, just not mentioned explicitly like the other variables, and might trip some people up a bit)

Collapse
 
amirhossein_veysi profile image
Amirhossein Veysi

Thanks for your comment, Yes seems that i forgot it, Thank you for mentioning.

Collapse
 
codewithpom profile image
Padmashree Jha

Awesome