You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: projects/build-a-pixel-art-creator-with-html-css-and-javascript/build-a-pixel-art-creator-with-html-css-and-javascript.mdx
+53-52Lines changed: 53 additions & 52 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,14 +15,13 @@ courses:
15
15
- javascript
16
16
tags:
17
17
- intermediate
18
-
- html
19
18
- css
20
19
- javascript
21
20
---
22
21
23
22
## Introduction
24
23
25
-
Ever wanted to make your own pixel art like the sprites in classic video games? What if you could build the entire drawing tool yourself, right in the browser?
24
+
Ever wanted to make your own pixel art like the sprites in classic video games like Super Mario and indie games like Stardew Valley? What if you could build the entire drawing tool yourself, right in the browser?
26
25
27
26
In this project tutorial, we'll build a **Pixel Art Creator**, a fully functional drawing app where you can paint pixel art on a grid canvas, pick colors, fill regions, and export your creation as a PNG image!
28
27
@@ -32,7 +31,7 @@ You will learn about the following concepts in this tutorial:
32
31
- How to use **2D arrays** to represent a grid of pixels in memory.
33
32
- How to handle **mouse events** and map screen coordinates to grid cells.
34
33
- How **flood fill** works (the algorithm behind the paint bucket tool).
35
-
- How to **export a canvas** as a downloadable PNG image.
34
+
- How to **export a canvas** as a downloadable **.png** image.
36
35
37
36
By the end of this tutorial, we'll have a fully functional pixel art editor that looks like this:
38
37
@@ -53,15 +52,16 @@ Before we jump into code, let's understand the core ideas behind any pixel art e
53
52
54
53
### The Grid as a 2D Array
55
54
56
-
A pixel art canvas is just a grid of colored squares. In code, we represent this grid as a **2D array**, an array of arrays:
55
+
A pixel art canvas is just a grid of colored squares. In code, we represent this grid as a **2D array** which is an array of arrays like so:
57
56
58
57
```javascript
59
58
// A 3x3 grid example
59
+
60
60
[
61
61
["#ffffff", "#ff0000", "#ffffff"],
62
62
["#ff0000", "#ff0000", "#ff0000"],
63
63
["#ffffff", "#ff0000", "#ffffff"],
64
-
];
64
+
]
65
65
```
66
66
67
67
Each value is a hex color string. The position in the array corresponds to a cell on the grid: `grid[row][col]` gives us the color at that position.
@@ -70,8 +70,8 @@ Each value is a hex color string. The position in the array corresponds to a cel
70
70
71
71
The HTML5 `<canvas>` element gives us a drawing surface. We use its 2D rendering context to draw rectangles, one per grid cell:
72
72
73
-
-`fillRect(x, y, width, height)` draws a filled rectangle.
74
-
-`strokeRect(x, y, width, height)` draws a rectangle outline (our grid lines).
73
+
-`.fillRect(x, y, width, height)` draws a filled rectangle.
74
+
-`.strokeRect(x, y, width, height)` draws a rectangle outline (our grid lines).
75
75
76
76
### Coordinate Mapping
77
77
@@ -86,15 +86,15 @@ Download the starter code from this repository: [Pixel Art Creator Starter Code]
**Note:** We'll be working in the **starter** directory. The **completed** directory contains the finished code you can use as reference if you get stuck.
@@ -105,7 +105,7 @@ Let's take a look at what the starter code gives us:
105
105
-**style.css** - All the styling is done. Dark theme, flexbox sidebar layout, tool button styles with active/hover states, a CSS Grid color palette, and responsive rules for mobile. No changes needed here.
106
106
-**script.js** - This is where we'll spend our time. The starter has variable declarations and function stubs with TODO comments. We'll implement each function step by step.
107
107
108
-
Open **starter/index.html** in your browser. You should see a dark-themed layout with a toolbar on the left and an empty canvas area:
108
+
Open **starter/index.html** in your browser. You should see a dark-themed layout with a toolbar on the left and an empty canvas area. Nothing is interactive yet - that's what JavaScript is for!
@@ -134,16 +134,19 @@ Find the `init()` function in the starter code and add the following code:
134
134
```javascript
135
135
functioninit() {
136
136
grid =Array.from({ length: gridSize }, () =>
137
-
Array(gridSize).fill("#ffffff"),
137
+
Array(gridSize).fill("#ffffff")
138
138
);
139
+
139
140
cellSize =Math.floor(480/ gridSize);
141
+
140
142
canvas.width= gridSize * cellSize;
141
143
canvas.height= gridSize * cellSize;
144
+
142
145
render();
143
146
}
144
147
```
145
148
146
-
**How this works:**
149
+
How this works:
147
150
148
151
-`Array.from({ length: gridSize }, () => Array(gridSize).fill("#ffffff"))` creates a 2D array where every cell starts as white (`#ffffff`). For a 16x16 grid, that's 256 cells.
149
152
-`cellSize = Math.floor(480 / gridSize)` calculates the pixel size of each cell. We target roughly 480 pixels total, so for a 16x16 grid each cell is 30px, for a 32x32 grid each cell is 15px, and so on.
- The nested loop goes through every cell in the grid, row by row, column by column.
184
-
- For each cell, we set `fillStyle` to that cell's color and draw a filled rectangle at the correct position.
188
+
- For each cell, we set `.fillStyle` to that cell's color and draw a filled rectangle at the correct position.
185
189
- We then draw a thin gray outline (`strokeRect`) to show the grid lines.
186
190
- The `hoveredCell` block draws a semi-transparent preview of the current color on whatever cell the mouse is hovering over. We use `globalAlpha = 0.4` to make it translucent, then reset it to `1.0` so future drawing isn't affected.
187
191
188
-
**Note:** We don't render the hover preview while actively drawing (`!isDrawing`) - it would be distracting during fast drawing.
192
+
**Note:** We don't render the hover preview while actively drawing (`!isDrawing`). It would be distracting during fast drawing.
189
193
190
194
The `init()` call is already at the bottom of the starter file, so once we implement these functions, they'll work right away!
191
195
@@ -195,7 +199,7 @@ Now let's make the canvas interactive. We need to figure out which cell the mous
195
199
196
200
### Step 2-a: Map Mouse Position to Grid Cell
197
201
198
-
Find the `getCellFromMouse` function and add this:
202
+
Find the `getCellFromMouse` function and add the following code:
199
203
200
204
```javascript
201
205
functiongetCellFromMouse(e) {
@@ -212,16 +216,16 @@ function getCellFromMouse(e) {
212
216
}
213
217
```
214
218
215
-
**How this works:**
219
+
How this works:
216
220
217
-
-`canvas.getBoundingClientRect()` tells us where the canvas is on the page.
218
-
- We subtract the canvas position from the mouse position to get coordinates _relative to the canvas_.
221
+
-`canvas.getBoundingClientRect()` tells us where the canvas is on the web page.
222
+
- We subtract the canvas position from the mouse position to get coordinates relative to the canvas.
219
223
- Dividing by `cellSize` and using `Math.floor` converts pixel coordinates to grid coordinates.
220
224
- If the position is outside the grid, we return `null` to avoid errors.
221
225
222
226
### Step 2-b: Paint a Single Cell
223
227
224
-
Now find `paintCell` and add:
228
+
Now find `paintCell` and implement it:
225
229
226
230
```javascript
227
231
functionpaintCell(row, col) {
@@ -324,7 +328,7 @@ function floodFill(row, col, newColor) {
324
328
}
325
329
```
326
330
327
-
**Let's break this down:**
331
+
Let's break this down:
328
332
329
333
- First, we check what color the clicked cell is (`targetColor`). If it's already the new color, we do nothing - this prevents an infinite loop!
330
334
- We use a **stack** (a regular JavaScript array used as a stack with `push` and `pop`) to track which cells to check next.
@@ -343,6 +347,8 @@ function floodFill(row, col, newColor) {
343
347
alt="Flood fill tool filling a smiley face with yellow"
344
348
/>
345
349
350
+
Phew… that was a little tough to implement! But, we're now through the most difficult part. 😤
351
+
346
352
## Step 4: Build the Color Palette and Tool Switching
347
353
348
354
Now let's make the toolbar interactive - generating color swatches, handling color selection, and switching between tools.
@@ -357,59 +363,56 @@ function buildPalette() {
357
363
PRESET_COLORS.forEach((color) => {
358
364
constswatch=document.createElement("div");
359
365
swatch.classList.add("color-swatch");
366
+
360
367
if (color === currentColor) swatch.classList.add("active");
- We loop through `PRESET_COLORS` and create a `<div>` for each one.
378
385
- Each swatch gets its background color set and a click handler.
379
386
- When clicked, we update `currentColor`, sync the custom color picker input, and toggle the `.active` class so only the selected swatch has the white border.
380
387
381
388
### Step 4-b: Custom Color Picker
382
389
383
-
Find the comment marked `Step 4-b` and add the custom color picker handler:
390
+
Find the TODO marked `Step 4-b` and add the custom color picker handler:
- When the dropdown value changes, we show a confirmation dialog - because changing the grid size resets everything.
441
444
- If confirmed, we parse the new value (`"16"`, `"32"`, or `"64"` - these are strings from the HTML, so we need `parseInt`), update `gridSize`, and call `init()` to rebuild the grid from scratch.
We don't export the visible canvas directly because it has grid lines drawn on it. Instead, we create a new, invisible canvas and draw only the colored cells - no grid lines. This gives us a clean exported image.
485
488
486
-
**How the export works:**
489
+
How the export works:
487
490
488
491
-`Math.max(16, Math.floor(512 / gridSize))` ensures the exported image is at least 512px wide. A 16x16 grid exports at 32px per cell (512px total), while a 64x64 grid exports at 8px per cell.
489
492
- We loop through the grid and draw each cell's color using `fillRect`, same as rendering, but without the `strokeRect` grid lines.
@@ -503,12 +506,10 @@ Test everything:
503
506
504
507
## Conclusion
505
508
506
-
Congratulations! You've built a pixel art editor from scratch using only HTML, CSS, and JavaScript!
509
+
Congratulations! You've built a pixel art editor from scratch using only HTML, CSS, and JavaScript! 🎉
507
510
508
511
Let's recap what we learned:
509
512
510
-
### Concepts Covered
511
-
512
513
-**Canvas API** - Using `fillRect`, `strokeRect`, `toDataURL`, and `getContext("2d")` to draw and export graphics.
513
514
-**2D Arrays** - Representing grid state in memory and keeping it in sync with the visual canvas.
514
515
-**Event Handling** - Using `mousedown`, `mousemove`, `mouseup`, and `mouseleave` for click-and-drag drawing.
@@ -517,7 +518,7 @@ Let's recap what we learned:
517
518
-**File Downloads** - Programmatically creating download links with `toDataURL()`.
518
519
-**Responsive CSS** - Using `@media` queries to adapt layouts for different screen sizes.
519
520
520
-
### Next Steps
521
+
### Ideas to Keep Building
521
522
522
523
Now that you have a working pixel art editor, here are some ways to extend it:
523
524
@@ -534,4 +535,4 @@ Now that you have a working pixel art editor, here are some ways to extend it:
Share your pixel art creations on Instagram and tag [@gokucodes](https://www.instagram.com/gokucodes/) and [@codedex_io](https://www.instagram.com/codedex.io/)!
538
+
Share your pixel art creations on Instagram and LinkedIn and tag [@gokucodes](https://www.instagram.com/gokucodes) and [@codedex_io](https://www.instagram.com/codedex.io/)!
0 commit comments