Introduction
A web page drawing application is a great opportunity to practice working with HTML, CSS, and JavaScript, as well as to create interactive tools. In this article, we will create a simple drawing application that will include tools for drawing various shapes, choosing colors, and saving the created image. The application will have a user-friendly interface with several tools, brush size and color settings.
For the full source code and possibly additional improvements, check out our GitHub.

Preparation
Before we start writing code, let's make sure we have the necessary components:
HTML — for page structure.
CSS — for interface styling.
JavaScript — for drawing, event processing, and saving images.
We will also need icons for the buttons that will be used to select drawing tools. These icons will be loaded in our project through the files in the icons folder.
Creating HTML and connecting photos and files
Let's start by creating an HTML page. This will be the main structure of our application.
HTML creates the page structure and sets the markup for all the elements that will be displayed in the application. Here is a more detailed description of each element in the HTML code:
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Приложение для рисования</title>
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="script.js" defer></script>
</head>
<body>
<div class="container">
<!-- Панель инструментов -->
<section class="tools-board">
<div class="row">
<label class="title">Формы</label>
<ul class="options">
<!-- Инструмент для рисования прямоугольника -->
<li class="option tool" id="rectangle">
<img src="icons/rectangle.svg" alt="Rectangle">
<span>Прямоугольник</span>
</li>
<!-- Инструмент для рисования круга -->
<li class="option tool" id="circle">
<img src="icons/circle.svg" alt="Circle">
<span>Круг</span>
</li>
<!-- Инструмент для рисования треугольника -->
<li class="option tool" id="triangle">
<img src="icons/triangle.svg" alt="Triangle">
<span>Треугольник</span>
</li>
<li class="option">
<input type="checkbox" id="fill-color">
<label for="fill-color">Цвет заливки</label>
</li>
</ul>
</div>
<div class="row">
<label class="title">Опции</label>
<ul class="options">
<!-- Инструмент кисть -->
<li class="option active tool" id="brush">
<img src="icons/brush.svg" alt="Brush">
<span>Щетка</span>
</li>
<!-- Инструмент ластик -->
<li class="option tool" id="eraser">
<img src="icons/eraser.svg" alt="Eraser">
<span>Ластик</span>
</li>
<!-- Ползунок для изменения размера кисти -->
<li class="option">
<input type="range" id="size-slider" min="1" max="30" value="5">
</li>
</ul>
</div>
<div class="row colors">
<label class="title">Цвета</label>
<ul class="options">
<!-- Кнопки выбора цвета -->
<li class="option"></li>
<li class="option selected"></li>
<li class="option"></li>
<li class="option"></li>
<li class="option">
<input type="color" id="color-picker" value="#4A98F7">
</li>
</ul>
</div>
<div class="row buttons">
<!-- Кнопки очистки и сохранения -->
<button class="clear-canvas">Очистить</button>
<button class="save-img">Сохранить как фото</button>
</div>
</section>
<!-- Холст для рисования -->
<section class="drawing-board">
<canvas></canvas>
</section>
</div>
</body>
</html>HTML explanation:
<section class="tools-board">:This is a toolbar that contains several options for the user. Here you can choose the shape (rectangle, circle, triangle), brush color, brush size, and other settings.
<ul class="options">:Lists (
<ul>) are created inside the toolbar, each of which represents a group of options. This is a list of buttons for selecting a shape (rectangle, circle, etc.), changing the color, brush size, and other settings.
<canvas>:This is the main canvas on which the drawing will take place. It does not contain default content and uses JavaScript for drawing.
Buttons and sliders:
The "Clear" and "Save as photo" buttons allow you to clear the canvas and save the image, respectively. The slider (
<input type="range">) allows you to change the size of the brush.
Color buttons:
In the colors section there are several buttons for selecting the color of the brush. Each button represents a color, and the last element is an element for selecting a color using a standard color picker.
Styling
Now that the HTML structure is ready, you can move on to styling with CSS. Here is an example of styles for our application:
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
}
body {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: #040925;
}
.container {
display: flex;
width: 100%;
gap: 10px;
padding: 10px;
max-width: 1050px;
}
section {
background: #fff;
border-radius: 7px;
}
.tools-board {
width: 210px;
padding: 15px 22px 0;
}
.tools-board .row {
margin-bottom: 20px;
}
.row .options {
list-style: none;
margin: 10px 0 0 5px;
}
.row .options .option {
display: flex;
cursor: pointer;
align-items: center;
margin-bottom: 10px;
}
.option:is(:hover, .active) img {
filter: invert(17%) sepia(90%) saturate(3000%) hue-rotate(900deg) brightness(100%) contrast(100%);
}
.option :where(span, label) {
color: #5A6168;
cursor: pointer;
padding-left: 10px;
}
.option:is(:hover, .active) :where(span, label) {
color: #4A98F7;
}
.option #fill-color {
cursor: pointer;
height: 14px;
width: 14px;
}
#fill-color:checked ~ label {
color: #4A98F7;
}
.option #size-slider {
width: 100%;
height: 5px;
margin-top: 10px;
}
.colors .options {
display: flex;
justify-content: space-between;
}
.colors .option {
height: 20px;
width: 20px;
border-radius: 50%;
margin-top: 3px;
position: relative;
}
.colors .option:nth-child(1) {
background-color: #fff;
border: 1px solid #bfbfbf;
}
.colors .option:nth-child(2) {
background-color: #000;
}
.colors .option:nth-child(3) {
background-color: #E02020;
}
.colors .option:nth-child(4) {
background-color: #6DD400;
}
.colors .option:nth-child(5) {
background-color: #4A98F7;
}
.colors .option.selected::before {
position: absolute;
content: "";
top: 50%;
left: 50%;
height: 12px;
width: 12px;
background: inherit;
border-radius: inherit;
border: 2px solid #fff;
transform: translate(-50%, -50%);
}
.colors .option:first-child.selected::before {
border-color: #ccc;
}
.option #color-picker {
opacity: 0;
cursor: pointer;
}
.buttons button {
width: 100%;
color: #fff;
border: none;
outline: none;
padding: 11px 0;
font-size: 0.9rem;
margin-bottom: 13px;
background: none;
border-radius: 4px;
cursor: pointer;
}
.buttons .clear-canvas {
color: #6C757D;
border: 1px solid #6C757D;
transition: all 0.3s ease;
}
.clear-canvas:hover {
color: #fff;
background: #6C757D;
}
.buttons .save-img {
background: #4A98F7;
border: 1px solid #4A98F7;
}
.drawing-board {
flex: 1;
overflow: hidden;
}
.drawing-board canvas {
width: 100%;
height: 100%;
}
This CSS styles elements such as tool buttons, colors, and sizes, creating a modern and user-friendly interface.
Writing logic in JavaScript
Now let's move on to the most interesting part - the drawing functionality. We will use JavaScript to handle events related to drawing on the canvas. The JavaScript code is connected through the script.js file, which we will consider below:
The JavaScript code controls the functionality of drawing on the canvas, interacts with the user, and updates the display depending on the selected settings. Let's look at the details of each code snippet.
Initialization of variables:
const canvas = document.querySelector("canvas"),
toolBtns = document.querySelectorAll(".tool"),
fillColor = document.querySelector("#fill-color"),
sizeSlider = document.querySelector("#size-slider"),
colorBtns = document.querySelectorAll(".colors .option"),
colorPicker = document.querySelector("#color-picker"),
clearCanvas = document.querySelector(".clear-canvas"),
saveImg = document.querySelector(".save-img"),
ctx = canvas.getContext("2d");
let prevMouseX, prevMouseY, snapshot,
isDrawing = false,
selectedTool = "brush",
brushWidth = 5,
selectedColor = "#000";canvas: Refers to the<canvas>element on the page where the drawing will take place.toolBtns: List of all tool buttons (brush, eraser, and shapes) that the user can interact with.fillColor: Variable for tracking the state of the "Fill color" checkbox.sizeSlider: Link to the slider for selecting the brush size.colorBtns: Variable for all color selection buttons that the user can click to select a color.colorPicker: Link to the standard HTML color picker.clearCanvasandsaveImg: Buttons for clearing the canvas and saving the image.ctx: The context of drawing a canvas, with which lines, shapes, and other operations are performed.
Setting the canvas background:
const setCanvasBackground = () => {
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = selectedColor;
}This code sets a white background for the canvas, then restores the current brush color for drawing.
Initializing the canvas after loading the page:
window.addEventListener("load", () => {
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
setCanvasBackground();
});When the page loads, the canvas size is set to match its current size on the screen (via offsetWidth and offsetHeight). Then the function is called to set the background.
Event handlers:
Tool selection handlers (brush, eraser and shapes):
toolBtns.forEach(btn => {
btn.addEventListener("click", () => {
document.querySelector(".options .active").classList.remove("active");
btn.classList.add("active");
selectedTool = btn.id;
});
});Each button of the (toolBtns) tool adds a click event handler that makes the button active by adding the active class and changes the selected tool by updating the selectedTool variable.
Brush resizing:
sizeSlider.addEventListener("change", () => brushWidth = sizeSlider.value);When the user changes the value of the slider, the brush size is updated.
Color change:
colorBtns.forEach(btn => {
btn.addEventListener("click", () => {
document.querySelector(".options .selected").classList.remove("selected");
btn.classList.add("selected");
selectedColor = window.getComputedStyle(btn).getPropertyValue("background-color");
});
});Each color selection button updates the current brush color (selectedColor) and makes the selected button active by adding the selected class.
Using the color picker:
colorPicker.addEventListener("change", () => {
colorPicker.parentElement.style.background = colorPicker.value;
colorPicker.parentElement.click();
});When the user selects a color through the standard HTML picker, the color is updated on the button as well.
Cleaning the canvas:
clearCanvas.addEventListener("click", () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
setCanvasBackground();
});
When you click the "Clear" button, the entire canvas is cleared and the background is restored.
Saving image:
saveImg.addEventListener("click", () => {
const link = document.createElement("a");
link.download = `${Date.now()}.jpg`;
link.href = canvas.toDataURL();
link.click();
});When you click the "Save as photo" button, a temporary link is created to download the image, converting the contents of the canvas to the data URL format.
Drawing on canvas:
const startDraw = (e) => {
isDrawing = true;
prevMouseX = e.offsetX;
prevMouseY = e.offsetY;
ctx.beginPath();
ctx.lineWidth = brushWidth;
ctx.strokeStyle = selectedColor;
ctx.fillStyle = selectedColor;
snapshot = ctx.getImageData(0, 0, canvas.width, canvas.height);
}
const drawing = (e) => {
if (!isDrawing) return;
ctx.putImageData(snapshot, 0, 0);
if (selectedTool === "brush" || selectedTool === "eraser") {
ctx.strokeStyle = selectedTool === "eraser" ? "#fff" : selectedColor;
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
} else if (selectedTool === "rectangle") {
drawRect(e);
} else if (selectedTool === "circle") {
drawCircle(e);
} else {
drawTriangle(e);
}
}
canvas.addEventListener("mousedown", startDraw);
canvas.addEventListener("mousemove", drawing);
canvas.addEventListener("mouseup", () => isDrawing = false);startDraw: When the user clicks on the canvas, drawing begins. The initial coordinates are saved and a snapshot of the current state of the canvas is created (for cancellation).drawing: When the user moves the mouse, drawing is performed depending on the selected tool.
In general, HTML creates the interface and structure, and JavaScript manages all the functionality of drawing, changing tools and saving the drawing.
Great. And this is what the entire java script code looks like:
const canvas = document.querySelector("canvas"),
toolBtns = document.querySelectorAll(".tool"),
fillColor = document.querySelector("#fill-color"),
sizeSlider = document.querySelector("#size-slider"),
colorBtns = document.querySelectorAll(".colors .option"),
colorPicker = document.querySelector("#color-picker"),
clearCanvas = document.querySelector(".clear-canvas"),
saveImg = document.querySelector(".save-img"),
ctx = canvas.getContext("2d");
let prevMouseX, prevMouseY, snapshot,
isDrawing = false,
selectedTool = "brush",
brushWidth = 5,
selectedColor = "#000";
const setCanvasBackground = () => {
ctx.fillStyle = "#fff";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = selectedColor;
}
window.addEventListener("load", () => {
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
setCanvasBackground();
});
// Functions for drawing shapes
const drawRect = (e) => {
if (!fillColor.checked) {
return ctx.strokeRect(e.offsetX, e.offsetY, prevMouseX - e.offsetX, prevMouseY - e.offsetY);
}
ctx.fillRect(e.offsetX, e.offsetY, prevMouseX - e.offsetX, prevMouseY - e.offsetY);
}
const drawCircle = (e) => {
ctx.beginPath();
let radius = Math.sqrt(Math.pow((prevMouseX - e.offsetX), 2) + Math.pow((prevMouseY - e.offsetY), 2));
ctx.arc(prevMouseX, prevMouseY, radius, 0, 2 * Math.PI);
fillColor.checked ? ctx.fill() : ctx.stroke();
}
const drawTriangle = (e) => {
ctx.beginPath();
ctx.moveTo(prevMouseX, prevMouseY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.lineTo(prevMouseX * 2 - e.offsetX, e.offsetY);
ctx.closePath();
fillColor.checked ? ctx.fill() : ctx.stroke();
}
// Tool handlers
toolBtns.forEach(btn => {
btn.addEventListener("click", () => {
document.querySelector(".options .active").classList.remove("active");
btn.classList.add("active");
selectedTool = btn.id;
});
});
// Brush Treatments
sizeSlider.addEventListener("change", () => brushWidth = sizeSlider.value);
colorBtns.forEach(btn => {
btn.addEventListener("click", () => {
document.querySelector(".options .selected").classList.remove("selected");
btn.classList.add("selected");
selectedColor = window.getComputedStyle(btn).getPropertyValue("background-color");
});
});
colorPicker.addEventListener("change", () => {
colorPicker.parentElement.style.background = colorPicker.value;
colorPicker.parentElement.click();
});
// Cleaning the canvas
clearCanvas.addEventListener("click", () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
setCanvasBackground();
});
// Saving an image
saveImg.addEventListener("click", () => {
const link = document.createElement("a");
link.download = `${Date.now()}.jpg`;
link.href = canvas.toDataURL();
link.click();
});
// Drawing
const startDraw = (e) => {
isDrawing = true;
prevMouseX = e.offsetX;
prevMouseY = e.offsetY;
ctx.beginPath();
ctx.lineWidth = brushWidth;
ctx.strokeStyle = selectedColor;
ctx.fillStyle = selectedColor;
snapshot = ctx.getImageData(0, 0, canvas.width, canvas.height);
}
const drawing = (e) => {
if (!isDrawing) return;
ctx.putImageData(snapshot, 0, 0);
if (selectedTool === "brush" || selectedTool === "eraser") {
ctx.strokeStyle = selectedTool === "eraser" ? "#fff" : selectedColor;
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
} else if (selectedTool === "rectangle") {
drawRect(e);
} else if (selectedTool === "circle") {
drawCircle(e);
} else {
drawTriangle(e);
}
}
canvas.addEventListener("mousedown", startDraw);
canvas.addEventListener("mousemove", drawing);
canvas.addEventListener("mouseup", () => isDrawing = false);
This code implements:
Drawing various shapes (rectangle, circle, triangle).
Choice of tools, including brush, eraser, and fill options.
Changing the color and size of the brush.
Cleaning the canvas and saving the image.
Findings and conclusion
We have created a simple drawing application in HTML, CSS and JavaScript that allows you to draw various shapes, change the color and size of the brush, and save the image. This application can be expanded by adding additional tools, improving the interface and integrating with the server for saving and sharing drawings.
