HTML5 Canvas Drawing & Animation Guide
In this tutorial, you'll learn about HTML5 Canvas Drawing & Animation Guide. We cover key concepts, practical examples, and best practices.
HTML5 Canvas provides a pixel-level drawing surface for rendering shapes, text, images, and animations directly in the browser using JavaScript.
In this tutorial, you'll learn the HTML5 Canvas API — how to draw shapes, render text, manipulate pixels, and build smooth animations. Canvas is fundamental for data visualizations, games, image processing, and any application that needs pixel-level control. By the end, you'll build a real-time particle system for threat visualization.
Real-world use: Durga Antivirus Pro uses Canvas to render network traffic heatmaps and real-time scan progress visualizations. Doda Browser's built-in screenshot editor relies on Canvas for annotation tools.
flowchart TD A[Canvas API] --> B[Canvas Setup] B --> C[Canvas Context] C --> D[Shapes & Paths] C --> E[Text & Fonts] C --> F[Images & Video] C --> G[Pixel Data] D --> H[Animation Loop] H --> I[requestAnimationFrame] I --> J[Clear - Draw - Update] style A fill:#6366f1,color:#fff style C fill:#4af,color:#fff style J fill:#4a4,color:#fff
Canvas Setup
The <canvas> element creates a fixed-size drawing surface. You access its drawing context — either 2d for 2D graphics or webgl for 3D.
<canvas id="myCanvas" width="800" height="600">
Your browser does not support the canvas element.
</canvas>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
if (!ctx) {
throw new Error('Canvas 2D context not supported');
}
// High-DPI display handling
function setupCanvas(canvas, width, height) {
const dpr = window.devicePixelRatio || 1;
canvas.width = width * dpr;
canvas.height = height * dpr;
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
const ctx = canvas.getContext('2d');
ctx.scale(dpr, dpr);
return ctx;
}
setupCanvas(canvas, 800, 600);
Drawing Shapes
Canvas uses a path-based drawing model. You begin a path, define sub-paths, then stroke or fill.
// Rectangle
ctx.fillStyle = '#6366f1';
ctx.fillRect(10, 10, 100, 50);
ctx.strokeStyle = '#4f46e5';
ctx.lineWidth = 3;
ctx.strokeRect(130, 10, 100, 50);
// Circle via arc
ctx.beginPath();
ctx.arc(400, 60, 40, 0, Math.PI * 2);
ctx.fillStyle = '#f59e0b';
ctx.fill();
// Arc segment
ctx.beginPath();
ctx.arc(550, 60, 40, 0, Math.PI * 1.5);
ctx.lineTo(550, 60);
ctx.closePath();
ctx.fillStyle = '#ef4444';
ctx.fill();
// Line
ctx.beginPath();
ctx.moveTo(10, 150);
ctx.lineTo(200, 150);
ctx.strokeStyle = '#10b981';
ctx.lineWidth = 4;
ctx.stroke();
Expected output: A blue rectangle, a purple outlined rectangle, an orange circle, a red pie segment, and a green line rendered on the canvas.
Complex Paths
Use bezierCurveTo and quadraticCurveTo for smooth curves.
// Quadratic Bezier curve
ctx.beginPath();
ctx.moveTo(50, 250);
ctx.quadraticCurveTo(150, 100, 250, 250);
ctx.strokeStyle = '#6366f1';
ctx.lineWidth = 3;
ctx.stroke();
// Cubic Bezier curve
ctx.beginPath();
ctx.moveTo(300, 300);
ctx.bezierCurveTo(400, 100, 500, 400, 600, 250);
ctx.strokeStyle = '#ec4899';
ctx.lineWidth = 3;
ctx.stroke();
// Star shape
function drawStar(ctx, cx, cy, spikes, outerRadius, innerRadius) {
const step = Math.PI / spikes;
ctx.beginPath();
for (let i = 0; i < 2 * spikes; i++) {
const radius = i % 2 === 0 ? outerRadius : innerRadius;
const angle = i * step - Math.PI / 2;
const x = cx + Math.cos(angle) * radius;
const y = cy + Math.sin(angle) * radius;
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
}
ctx.closePath();
ctx.fillStyle = '#f59e0b';
ctx.fill();
ctx.strokeStyle = '#d97706';
ctx.lineWidth = 2;
ctx.stroke();
}
drawStar(ctx, 400, 200, 5, 80, 40);
Expected output: A quadratic curve, a cubic bezier curve, and a filled five-pointed star shape.
Text Rendering
Canvas renders text with fillText and strokeText, supporting font styling and alignment.
ctx.font = 'bold 48px Inter, system-ui, sans-serif';
ctx.fillStyle = '#1e293b';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText('DodaTech', 400, 150);
ctx.font = '24px Inter, sans-serif';
ctx.fillStyle = '#64748b';
ctx.fillText('Canvas Graphics Engine', 400, 200);
// Rotated text
ctx.save();
ctx.translate(400, 300);
ctx.rotate(-0.1);
ctx.font = '16px monospace';
ctx.fillStyle = '#475569';
ctx.fillText('Built by Doda Browser developers', 0, 0);
ctx.restore();
Expected output: "DodaTech" in bold 48px centered text, a subtitle, and rotated small text below.
Image Drawing
Load and draw images onto the canvas, with optional clipping and scaling.
const img = new Image();
img.src = '/images/logo.png';
img.onload = function() {
// Draw image at original size
ctx.drawImage(img, 50, 400);
// Draw scaled
ctx.drawImage(img, 300, 400, 150, 75);
// Clip into circle
ctx.beginPath();
ctx.arc(550, 450, 50, 0, Math.PI * 2);
ctx.closePath();
ctx.clip();
ctx.drawImage(img, 500, 400, 100, 100);
};
Pixel Manipulation
Access and modify individual pixel data using getImageData and putImageData.
function applyGrayscale(canvas) {
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const gray = 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];
data[i] = gray; // Red
data[i + 1] = gray; // Green
data[i + 2] = gray; // Blue
// data[i + 3] is alpha - unchanged
}
ctx.putImageData(imageData, 0, 0);
}
function applySepia(canvas) {
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
data[i] = Math.min(255, r * 0.393 + g * 0.769 + b * 0.189);
data[i + 1] = Math.min(255, r * 0.349 + g * 0.686 + b * 0.168);
data[i + 2] = Math.min(255, r * 0.272 + g * 0.534 + b * 0.131);
}
ctx.putImageData(imageData, 0, 0);
}
Animation Loop
Animations use requestAnimationFrame to sync with the display refresh rate.
const canvas = document.getElementById('animation');
const animationCtx = canvas.getContext('2d');
let frame = 0;
function animate() {
animationCtx.clearRect(0, 0, canvas.width, canvas.height);
// Bouncing ball
const x = 400 + Math.sin(frame * 0.02) * 300;
const y = 300 + Math.cos(frame * 0.03) * 200;
animationCtx.beginPath();
animationCtx.arc(x, y, 25, 0, Math.PI * 2);
animationCtx.fillStyle = '#6366f1';
animationCtx.fill();
// Trail effect (semi-transparent clear instead of full clear)
animationCtx.fillStyle = 'rgba(255, 255, 255, 0.1)';
animationCtx.fillRect(0, 0, canvas.width, canvas.height);
frame++;
requestAnimationFrame(animate);
}
animate();
Particle System
A real-world particle system for visualizing network threats.
class Particle {
constructor(x, y) {
this.x = x;
this.y = y;
this.vx = (Math.random() - 0.5) * 4;
this.vy = (Math.random() - 0.5) * 4 - 2;
this.size = Math.random() * 4 + 1;
this.life = 1;
this.decay = 0.005 + Math.random() * 0.01;
this.hue = 200 + Math.random() * 60; // Blue-green range for safe traffic
}
update() {
this.x += this.vx;
this.y += this.vy;
this.vy += 0.05; // gravity
this.life -= this.decay;
}
draw(ctx) {
ctx.globalAlpha = this.life;
ctx.fillStyle = `hsl(${this.hue}, 70%, 50%)`;
ctx.beginPath();
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
ctx.fill();
}
}
class NetworkVisualizer {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.particles = [];
this.running = false;
}
emit(count = 3) {
for (let i = 0; i < count; i++) {
this.particles.push(
new Particle(
this.canvas.width / 2,
this.canvas.height - 20
)
);
}
}
update() {
this.ctx.fillStyle = 'rgba(15, 23, 42, 0.15)';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
this.particles.forEach(p => {
p.update();
p.draw(this.ctx);
});
this.particles = this.particles.filter(p => p.life > 0);
if (this.running) {
requestAnimationFrame(() => this.update());
}
}
start() {
this.running = true;
// Emit particles periodically
setInterval(() => this.emit(5), 200);
this.update();
}
}
const viz = new NetworkVisualizer(document.getElementById('network-viz'));
viz.start();
Expected output: A dark canvas with glowing particles rising from the bottom center, fading out as they drift, simulating network traffic flow.
Common Errors
- Canvas size mismatch — CSS dimensions differ from canvas attribute dimensions. Always match
canvas.width/heightattributes with display size, especially on HiDPI displays. - Forgetting
ctx.beginPath()— Without a new path, subsequent draw calls accumulate on the previous path. Always start a new path before each shape. - Missing
onloadfor images —drawImagebefore the image loads draws nothing. Always use theloadevent or checkimg.complete. getImageDataCORS error — Drawing images from external domains withoutcrossOrigin="anonymous"taints the canvas, blockinggetImageData.- Not clearing the canvas — Animations accumulate previous frames. Use
clearRector a semi-transparent fill to create trails without smearing.
Practice Questions
Challenge
Build a canvas-based real-time packet visualizer. Emit colored particles based on packet type (green for safe, yellow for suspicious, red for threats). Add mouse interaction so clicking emits a burst of particles. Display a live particle count.
Real-World Task
You are building the threat detection dashboard for Durga Antivirus Pro. Use Canvas to render a heatmap showing which file system regions are being scanned. Overlay a particle system that flows from the scan origin outward. Connected nodes represent detected threats, with color intensity indicating severity. The animation must maintain 60fps using requestAnimationFrame and support HiDPI displays.
Related: Web Animations Guide | Previous: JavaScript Fundamentals | Next: Web Components
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro