pixel art editor banner

Build a Pixel Art Editor with HTML Canvas: Step-by-Step Tutorial

Get ready to unleash your inner artist and code your own interactive pixel art editor. This tutorial unlocks the keys to building your own pixel art editor using HTML Canvas and JavaScript. We'll guide you through every step, from creating responsive layouts and customizable color palettes to implementing intuitive drawing mechanics. No coding experience? No worries! This journey is designed for both beginners and aspiring pixel magicians. Are you ready to code your creative vision into reality? Dive in and let's build your pixel playground!

Overview

This step-by-step tutorial equips you with the code and skills to build your own interactive pixel art editor using HTML Canvas and JavaScript. No prior experience? No problem. We'll guide you through everything from layout design and customizable color palettes to click-and-drag drawing magic.

By the end, you'll:

  • Craft a responsive pixel playground to unleash your creativity.
  • Master JavaScript Canvas and bring your pixel visions to life.
  • Customize your palette to fuel your artistic expression.
  • Draw with ease using intuitive click-and-drag mechanics.

Our pixel art editor will have the following features and capabilities.

  • Responsive UI: A canvas element which adapts to different screen sizes
  • Choose colors from a palette: Select different colors to paint pixels.
  • Click and drag to paint: Clicking and dragging will fill adjacent pixels with the chosen color.
  • Clear the canvas: Reset the drawing area for a fresh start.
  • Save the artwork: Download the created pixel art as an image.

Here's a preview of how our pixel art editor will look:


pixel art editor

Now, for the fun part. Let's write some code

1. HTML Setup:

  • Create an HTML file and add basic structure with elements like <header>, <main>, and <footer>.
  • In <main>, include a <canvas> element with an ID (#pixel-canvas).
  • In the footer add three sections: one div for the color palette (#color-palette), another for our buttons (#buttons-container) and a paragraph for some footer text

Copy the following into your HTML file

HTML
                        
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Asaqeni Pixel Art Editor</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>Asaqeni Pixel Art Editor</h1>
    </header>
    <main>
        <canvas id="pixel-canvas"></canvas>
    </main>
    <footer>
        <div id="options-container">
            <div id="color-palette"></div>
            <div id="buttons-container">
                <button id="clear-button">Clear Canvas</button>
                <button id="save-button">Save as jpg</button>
            </div>
            <p id="footer-text">Made with ❤️ by Asaqeni Learning</p>
        </div>
    </footer>
    <script src="script.js"></script>
</body>
</html>

2. CSS Styling:

The next part is making sure our editor has a nice layout and is responsive. We will:

  • Create a CSS file and save it in the same folder as our html file. Save it with the name: style.css
  • Define styles for our page and specifically the canvas, palette, and buttons.
  • We want our canvas to be responsive so we will not give it any specific dimensions here. We will just give it a border and background color.
  • Style the palette and button for visual appeal.
style.css
                        
body {
  margin: 0;
  font-family: sans-serif;
}

header {
  background-color: black;
  color: #eee;
  margin: 0;
  padding: 16px;
  text-align: center;
}

main {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 20px;
}

footer {
  position: fixed;
  width: 100%;
  bottom: 0;
  text-align: center;
  padding: 20px;
}

#pixel-canvas {
  border: 1px solid #ddd;
  background-color: #eee;
}

#options-container {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
}

#color-palette {
  display: flex;
  flex-wrap: wrap;
  flex: 0 0 300px;
  margin-right: 10px;
  margin: 10px 0;
}

#color-palette button {
  width: 20px;
  height: 20px;
  margin: 5px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

#buttons-container {
  width: 300px;
  text-align: right;
  padding-right: 45px;
}

#clear-button, #save-button {
  display: inline-block;
  background-color: #eee;
  border: 1px solid #ddd;
  border-radius: 5px;
  padding: 5px 10px;
  cursor: pointer;
}

#options-container p {
  width: 100%;
  text-align: center;
  margin-top: 10px;
}

/* Making our layout responsive for mobile screens */
@media (max-width: 500px) {
  #color-palette,
  #buttons-container {
    width: 100%;
    margin: 10px auto;
    text-align: center;
  }

  #footer-text {
    display: none;
  }
}

3. JavaScript Functionality:

Now that we have our UI, let us give it some functionality.

You can see the full JavaScript code with comments towards the end of the article

Add a JavaScript file (script.js) to handle user interaction and drawing logic.

Accessing the DOM elements

We will need to access the DOM element from our HTML. We will get the elements using their respective IDs or tags.

JavaScript
                        
const canvas = document.getElementById("pixel-canvas");
const clearButton = document.getElementById("clear-button");
const saveButton = document.getElementById("save-button");
const header = document.querySelector("header");
const footer = document.querySelector("footer");

Accessing the canvas element

Use getContext("2d") to access the canvas drawing context.

JavaScript
                        
const ctx = canvas.getContext("2d");

Making our canvas responsive

Our layout has 3 sections i.e. header, main and footer. After our header and footer have been displayed, we want the canvas to take up the remaining height.

We will achieve this by calculating the height of our header and footer, then resizing the canvas to fit the available space.

We will create a function called resizeCanvas which will be called when the page loads as well as when it is resized.

JavaScript
                        
let headerHeight = header.offsetHeight;
let footerHeight = footer.offsetHeight;

window.addEventListener("resize", resizeCanvas);

resizeCanvas();

function resizeCanvas() {
  canvas.width = window.innerWidth - 50; // Or desired width
  canvas.height = window.innerHeight - headerHeight - footerHeight - 30; // Adjust for header and footer
}

Create the color Palette

Our palette items will be created using javascript. We will follow the steps below

  • Create an array with the name colors to store color values for the palette. You can use the hex values (e.g. #333) or the color names (e.g. green).
  • Create a color button for each color in our array
  • Add click event listeners to color palette buttons to set the current color.
  • Attach the buttons to the color palette.

We will use the following code:

JavaScript
                        
// Define an array of available colors for the pixel art
const colors = ["#000", "#fff", "#f00", "blue", "#0f0", "purple", "#ff0", "#f0f"];

// Add colors to the palette & Click event listeners for each color
colors.forEach((color) => {
  const button = document.createElement("button");
  button.style.backgroundColor = color;
  button.style.border = "1px solid #e0e0e0";
  button.addEventListener("click", () => {
    if (selectedButton !== null) {
      selectedButton.style.border = "none";
    }
    selectedColor = color;
    button.style.border = "2px solid black";
    selectedButton = button;
  });
  document.getElementById("color-palette").appendChild(button);
});

If you open or refresh your file in the browser, it will now look like this:


pixel art editor on a mobile screen

Implement click and drag functionality on the canvas:

The most important step is being able to draw on our canvas. We want to achieve the following:

  • On click, identify the clicked pixel and set its color.
  • On click + drag, identify all adjacent pixels and set their color as well.

Setting a default color, tracking if mouse is being dragged and getting co-ordinates.

Let's set the default color to the first color in our colors array colors[0], and set our color palette selection to null. We will also create a variable to check the dragging state as well as the co-ordinates of the mouse on the canvas. We will use the variables startX and startY for the x and y axis, respectively.

JavaScript
                        
let selectedColor = colors[0];
let selectedButton = null;
let isDragging = false;
let startX, startY;

Drawing a pixel of a selected color and size

Let's create a function with the name setColorAtPixel which will take two parameters for the x and y axis, respectively. we will use fillStyle to set the color, and fillRect to determine the size of the rectangle we are painting. For this tutorial we are using a size of 5 x 5 pixels.

JavaScript
                        
function setColorAtPixel(x, y) {
  // Ensure coordinates are within canvas bounds
  if (x >= 0 && x < canvas.width && y >= 0 && y < canvas.height) {
    // Round coordinates to nearest integer for pixel precision
    x = Math.round(x);
    y = Math.round(y);
    ctx.fillStyle = selectedColor;
    ctx.fillRect(x, y, 5, 5);
  }
}

If you want to implement different brush sizes, you simply need to dynamically change the values of fillRect to values of your choice e.g. ctx.fillRect(x, y, 20, 20)

Adding event listeners for clicking and dragging the mouse

We want to be able to draw when the user clicks or drags the mouse while the mouse is clicked. When the user is dragging the mouse and releases the mouse, we will stop drawing.

JavaScript
                        
canvas.addEventListener("mousedown", (event) => {
  if (event.button === 0) {
    isDragging = true;
    startX = event.offsetX;
    startY = event.offsetY;
    setColorAtPixel(startX, startY);
  }
});

canvas.addEventListener("mouseup", () => {
  isDragging = false;
});

canvas.addEventListener("mousemove", (event) => {
  if (isDragging) {
    setColorAtPixel(event.offsetX, event.offsetY);
  }
});

Add functionality for the clear button to reset the canvas.

We want users to be able to clear the canvas and start afresh whenever they want to. Let's add an eventListener to the clearButton to achieve this.

JavaScript
                        
clearButton.addEventListener("click", () => {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
});

You might have noticed that clearRect behaves like the opposite of fillRect. You can use the same logic to create an eraser. simply change the x and y co-ordinates and then set a size for your eraser e.g. ctx.clearRect(x, y, 5, 5);

Save the canvas as an image

The last step is to enable the user to save their file. We will achieve this by converting the canvas to a PNG data URL using the toDataURL() function. We will then create and use a link to initiate the download. we will set the canvas's data URL as the link's href attribute. We will then simulate a click for the download to start.

Please note that PNG and JPG image formats will behave differently when saving your file. JPG will give you only the drawn elements on a blank (black) background, while PNG format will give you your drawing with the canvas' background color. For this example we have used the PNG option

JavaScript
                        
saveButton.addEventListener("click", ()=>{
  save();
});

function save(){
  const dataURL = canvas.toDataURL("image/png");
  const link = document.createElement("a");
  link.href = dataURL;
  link.download = "asaqeni-pixel-art.png";
  link.click();
  link.remove();

}

If you open the pixel art editor in your browser, and click on it you will notice some changes. Try dragging your mouse and see it in action. You can select or change colors from your color palette by simply clicking on them. Take a look at my drawing below:


pixel art editor on a mobile screen

Combined JavaScript Code

Our script.js file looks like this:

style.js
                        
const canvas = document.getElementById("pixel-canvas");
const clearButton = document.getElementById("clear-button");
const saveButton = document.getElementById("save-button");
const header = document.querySelector("header");
const footer = document.querySelector("footer");

// Get the 2D drawing context from the canvas element
const ctx = canvas.getContext("2d");

// Calculate the heights of the header and footer elements
let headerHeight = header.offsetHeight;
let footerHeight = footer.offsetHeight;

// Define an array of available colors for the pixel art
const colors = ["#000", "#fff", "#f00", "blue", "#0f0", "purple", "#ff0", "#f0f"];

// Initialize variables for color and button selection
let selectedColor = colors[0]; // Start with the first color (black)
let selectedButton = null; // No button selected initially

// Flag to track whether the user is currently dragging on the canvas
let isDragging = false;

// Variables to store the starting X and Y coordinates of the drag
let startX, startY;

// Add event listener for window resizing
window.addEventListener("resize", resizeCanvas);

// Call resizeCanvas initially to set initial size
resizeCanvas();

// Add colors to the palette & Click event listeners for each color
colors.forEach((color) => {
  const button = document.createElement("button");
  button.style.backgroundColor = color;
  button.style.border = "1px solid #e0e0e0";
  button.addEventListener("click", () => {
    if (selectedButton !== null) {
      selectedButton.style.border = "none";
    }
    selectedColor = color;
    button.style.border = "2px solid black";
    selectedButton = button;
  });
  document.getElementById("color-palette").appendChild(button);
});

// Add event listeners for the "mousedown", "mouseup" and "mousemove" event on the canvas

canvas.addEventListener("mousedown", (event) => {
  // This code will execute when the user presses the mouse button on the canvas
  if (event.button === 0) { // Check for left mouse button
    isDragging = true;
    startX = event.offsetX; // Store the starting X coordinate of the mouse pointer within the canvas
    startY = event.offsetY; // Store the starting Y coordinate of the mouse pointer within the canvas
    setColorAtPixel(startX, startY);
  }
});

canvas.addEventListener("mouseup", () => {
  // This code will execute when the user releases the mouse button on the canvas
  isDragging = false;
});

canvas.addEventListener("mousemove", (event) => {
  // This code will execute when the user moves the mouse while it's over the canvas
  if (isDragging) {
    setColorAtPixel(event.offsetX, event.offsetY);
  }
});

clearButton.addEventListener("click", () => {
  ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the entire canvas
});

saveButton.addEventListener("click", ()=>{
  save();
});

// Function to draw a pixel of the selected color at a specific position on the canvas
function setColorAtPixel(x, y) {
  // Ensure coordinates are within canvas bounds
  if (x >= 0 && x < canvas.width && y >= 0 && y < canvas.height) {
    // Round coordinates to nearest integer for pixel precision
    x = Math.round(x);
    y = Math.round(y);
    ctx.fillStyle = selectedColor;
    ctx.fillRect(x, y, 5, 5); // Fill a 5x5 pixel rectangle
  }
}

// Function to resize the canvas element to fit its available space within the layout
function resizeCanvas() {
  canvas.width = window.innerWidth - 50; // Or desired width
  canvas.height = window.innerHeight - headerHeight - footerHeight - 30; // Adjust for header and footer
}

// save canvas as an image
function save(){
  // Convert canvas to a PNG data URL
  const dataURL = canvas.toDataURL("image/png");

  // Create a link element to initiate the download
  const link = document.createElement("a");

  // Set the link's href attribute to the data URL of the canvas image
  link.href = dataURL;
  link.download = "asaqeni-pixel-art.jpg"; // Suggested filename

  // Simulate a Click to Download
  link.click();

  // Clean up (optional)
  link.remove(); // Remove the link from the DOM after clicking

}

Congratulations! You've just built your own interactive pixel art playground, powered by code and fueled by your creative spark. From responsive layouts to click-and-drag magic, you've mastered the tools to bring your digital visions to life.

Don't stop here! Dive deeper into customization, experiment with animation, and share your creations with the world. Remember, the canvas is limitless, just like your artistic potential.

Keep coding, keep pixelating, and keep pushing the boundaries of your imagination!