How To Create a Drawing Application in JavaScript, HTML and CSS
In this JavaScript Tutorial, we will see how to create a Drawing application using JavaScript, HTML5, CSS3.
In this project tutorial we will build a painting project with multiple drawing tools, shapes, colors, and even undo/redo functionality.
What We Are Gonna Use In This Project:
- JavaScript Programming Language.- HTML and CSS.
- Font-awesome.
- Visual Studio Editor.
Project Source Code:
- Canvas History Recorder
Captures the current canvas state as a data URL and stores it in the undo stack for later restoration.
// Save the current drawing to history so we can undo later
function saveState() {
// Convert canvas to a data URL (like a photo of the drawing)
undoStack.push(canvas.toDataURL());
// Clear redo history when new drawing happens
// (can't redo after drawing something new)
redoStack = [];
}
- Canvas State Restorer
Takes a saved canvas state and redraws it on the canvas, clearing any existing content first.
// Put a saved drawing back on the canvas (for undo/redo)
function restoreState(state) {
// Create a new image from the saved data
const img = new Image();
img.src = state;
// When image loads, draw it on the canvas
img.onload = () => {
// Clear canvas first
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw the saved state
ctx.drawImage(img, 0, 0);
};
}
- Drawing Session Initiator
Activates drawing mode when mouse is pressed down and captures the starting coordinates for the drawing operation.
// Start drawing when mouse button is pressed down
function startDrawing(e) {
isDrawing = true;
// Get mouse position and remember it
[lastX, lastY] = [e.offsetX, e.offsetY];
// For shapes, take a snapshot of current canvas
// This helps us show a preview while dragging
if (currentShape !== 'freehand') {
snapshot = ctx.getImageData(0, 0, canvas.width, canvas.height);
}
}
- Main Drawing Function
Handles all drawing operations based on current tool and shape settings, managing different drawing modes like freehand, lines, rectangles, and circles.
// Do the actual drawing as mouse moves
function draw(e) {
// Don't do anything if we're not in drawing mode
if (!isDrawing) return;
// Get current mouse position
const x = e.offsetX;
const y = e.offsetY;
// Set color based on current tool
// If using eraser, use background color (to "erase")
// Otherwise use the selected drawing color
ctx.strokeStyle = currentTool === 'eraser' ? bgColor : currentColor;
ctx.fillStyle = currentTool === 'eraser' ? bgColor : currentColor;
// Set line thickness and style based on selected tool
if (currentTool === 'brush') {
// Brush is thicker than pencil
ctx.lineWidth = brushSize.value * 2;
ctx.lineCap = 'round'; // Round end caps for smooth lines
ctx.lineJoin = 'round'; // Round corners for smooth lines
} else if (currentTool === 'pencil' || currentTool === 'eraser') {
ctx.lineWidth = brushSize.value;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
}
// Do different drawing based on selected shape
switch (currentShape) {
case 'freehand':
// Free drawing - follow the mouse movement exactly
ctx.beginPath(); // Start a new line
ctx.moveTo(lastX, lastY); // Move to where we started
ctx.lineTo(x, y); // Draw to current position
ctx.stroke(); // Actually draw the line
[lastX, lastY] = [x, y]; // Update position for next move
break;
case 'line':
// Straight line - from start point to current mouse position
ctx.putImageData(snapshot, 0, 0); // Restore canvas to before drag
ctx.beginPath(); // Start a new line
ctx.moveTo(lastX, lastY); // Move to start position
ctx.lineTo(x, y); // Draw to current position
ctx.stroke(); // Actually draw the line
break;
case 'rectangle':
// Rectangle - from start point with width/height to mouse
ctx.putImageData(snapshot, 0, 0); // Restore canvas to before drag
const width = x - lastX; // Calculate width based on mouse
const height = y - lastY; // Calculate height based on mouse
// Fill the rectangle if fill option is on
if (fillShape) {
ctx.fillRect(lastX, lastY, width, height);
}
// Always draw the outline
ctx.strokeRect(lastX, lastY, width, height);
break;
case 'circle':
// Circle - center at start point, size to mouse position
ctx.putImageData(snapshot, 0, 0); // Restore canvas to before drag
// Calculate radius (distance from center to mouse)
const radius = Math.sqrt(Math.pow(x - lastX, 2) + Math.pow(y - lastY, 2));
ctx.beginPath(); // Start a new shape
ctx.arc(lastX, lastY, radius, 0, 2 * Math.PI); // Draw full circle
// Fill the circle if fill option is on
if (fillShape) { ctx.fill(); }
// Always draw the outline
ctx.stroke();
break;
}
}
- Drawing Session Finalizer
Deactivates drawing mode when mouse is released and saves the current state to the undo history.
// Stop drawing when mouse button is released
function stopDrawing() {
if (isDrawing) {
isDrawing = false;
// Save to history when finished drawing
// (so we can undo if needed)
saveState();
}
}
- Previous State Restorer
Moves backward through the drawing history by one step, allowing users to reverse their last action.
// Go back one step in drawing history
function undo() {
// Need at least 2 items in stack (original blank + at least one drawing)
if (undoStack.length > 1) {
// Move current drawing to redo stack
redoStack.push(undoStack.pop());
// Show the previous drawing
restoreState(undoStack[undoStack.length - 1]);
}
}
- Forward State Restorer
Moves forward through the drawing history by one step, allowing users to restore previously undone actions.
// Go forward one step in drawing history
function redo() {
if (redoStack.length > 0) {
// Get the most recent redo state
const stateToRedo = redoStack.pop();
// Add it back to the undo history
undoStack.push(stateToRedo);
// Show this drawing
restoreState(stateToRedo);
}
}
- Background Color Updater
Updates the canvas background color using the selected color from the color picker and refreshes the display.
// Change the canvas background color
function changeBackgroundColor() {
// Get color from the color picker
const newColor = customColorPicker.value;
bgColor = newColor;
// Apply by clearing canvas with new background color
clearCanvas();
}
- Canvas Content Eraser
Completely clears the canvas and fills it with the current background color, then saves this blank state.
// Clear everything from the canvas
function clearCanvas() {
// Fill entire canvas with background color
ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Save this blank state to history
saveState();
}
- Active Tool Selector
Updates the current tool setting and provides visual feedback by highlighting the selected tool button.
// Highlight the active tool button
function setActiveTool(toolName) {
currentTool = toolName;
// Remove highlight from all buttons first
document.querySelectorAll('.toolbar button').
forEach(btn => btn.classList.remove('active'));
// Add highlight to selected tool button
document.getElementById(`${toolName}Btn`).classList.add('active');
}
- Drawing Export Handler
Converts the current canvas content to a PNG image and triggers a download for the user.
// Save drawing as a PNG image file
function saveCanvas() {
// Create a download link
const link = document.createElement('a');
link.download = 'drawing.png'; // File name
// Get the canvas data as an image URL
link.href = canvas.toDataURL();
// Trigger the download
link.click();
}
if you want the source code click on the download button below
disclaimer: you will get the source code with the database script and to make it work in your machine is your responsibility and to debug any error/exception is your responsibility this project is for the students who want to see an example and read the code not to get and run.
Download Projects Source Code