Plotting Charts and Figures Get Certified
With the Canvas API you can create your own charts and figures from scratch (bar charts, line charts, pie charts and more) without using any external library. Every chart here follows the same recipe, scale the data to pixels, draw the shapes, then add labels.
Simple Bar Chart
A bar chart turns each number into a rectangle. The key idea is scaling: a value is converted to a height in pixels, and because canvas y coordinates grow downward, each bar is drawn upward from a baseline.
What this code does: Draws a vertical bar chart of six months of sales. Each value (0 to 100) is scaled to a bar height of up to 220 pixels, bars are spaced evenly along a baseline, and each bar gets its value above it and its month name below the axis. The text output lists, for every month, the value, the bar height in pixels and the y position of the bar’s top, so you can see exactly how the numbers become pixels.
const canvas = document.createElement("canvas");
canvas.width = 500;
canvas.height = 300;
const ctx = canvas.getContext("2d");
// White background so the chart is readable (and looks right if you export it)
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, 500, 300);
const data = [45, 70, 30, 90, 55, 80];
const labels = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"];
const barWidth = 50;
const gap = 20;
const startX = 40;
const baseY = 260;
const maxValue = 100;
const chartHeight = 220;
// Horizontal axis line
ctx.strokeStyle = "#94a3b8";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(startX - 10, baseY);
ctx.lineTo(480, baseY);
ctx.stroke();
data.forEach((value, i) => {
const barHeight = (value / maxValue) * chartHeight;
const x = startX + i * (barWidth + gap);
const y = baseY - barHeight;
// Bar
ctx.fillStyle = "#3b82f6";
ctx.fillRect(x, y, barWidth, barHeight);
// Value above the bar
ctx.fillStyle = "#1e293b";
ctx.font = "13px Arial";
ctx.textAlign = "center";
ctx.fillText(value, x + barWidth / 2, y - 6);
// Month label below the axis
ctx.fillText(labels[i], x + barWidth / 2, baseY + 20);
console.log(labels[i] + ": value " + value + " -> bar height " + barHeight.toFixed(0) + "px, top at y=" + y.toFixed(0));
});
showCanvas("Monthly sales bar chart", canvas);
// ===== Helper below is not part of the lesson =====
function showCanvas(label, source) {
const copy = document.createElement("canvas");
copy.width = source.width;
copy.height = source.height;
copy.getContext("2d").drawImage(source, 0, 0);
copy.style.maxWidth = "100%";
setTimeout(() => {
const box = document.createElement("div");
box.className = "demo-canvas-box";
box.innerHTML = "<p class='demo-canvas-label'>" + label + "</p>";
box.appendChild(copy);
document.getElementById("previewContent").appendChild(box);
}, 0);
}
Line Chart
A line chart plots one point per value and joins them with straight segments. It shows how a value changes over time.
What this code does: Plots a week of temperatures. Light grid lines with degree labels give a scale, a red line connects the seven daily points (straight segments between points, not a smooth curve), and each point is marked with a dot, its temperature and its day name. The text output reports the hottest day, the coldest day and the average, calculated from the same data.
const canvas = document.createElement("canvas");
canvas.width = 500;
canvas.height = 300;
const ctx = canvas.getContext("2d");
// White background so the chart is readable (and looks right if you export it)
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, 500, 300);
const temps = [22, 25, 19, 28, 30, 27, 24];
const days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const maxTemp = 35;
const startX = 50;
const baseY = 250;
const stepX = 60;
const chartHeight = 200;
// Light horizontal grid lines every 10 degrees
ctx.strokeStyle = "#e2e8f0";
ctx.lineWidth = 1;
ctx.fillStyle = "#64748b";
ctx.font = "11px Arial";
ctx.textAlign = "right";
for (let t = 0; t <= 30; t += 10) {
const gy = baseY - (t / maxTemp) * chartHeight;
ctx.beginPath();
ctx.moveTo(40, gy);
ctx.lineTo(470, gy);
ctx.stroke();
ctx.fillText(t + "°", 34, gy + 4);
}
// Draw the line through every point
ctx.beginPath();
ctx.strokeStyle = "#ef4444";
ctx.lineWidth = 3;
temps.forEach((temp, i) => {
const x = startX + i * stepX;
const y = baseY - (temp / maxTemp) * chartHeight;
if (i === 0) ctx.moveTo(x, y);
else ctx.lineTo(x, y);
});
ctx.stroke();
// Draw the points, values and day labels
temps.forEach((temp, i) => {
const x = startX + i * stepX;
const y = baseY - (temp / maxTemp) * chartHeight;
ctx.beginPath();
ctx.arc(x, y, 5, 0, Math.PI * 2);
ctx.fillStyle = "#ef4444";
ctx.fill();
ctx.fillStyle = "#1e293b";
ctx.font = "13px Arial";
ctx.textAlign = "center";
ctx.fillText(temp + "°", x, y - 12);
ctx.fillText(days[i], x, baseY + 20);
});
console.log("Hottest day:", days[temps.indexOf(Math.max(...temps))], Math.max(...temps) + "°");
console.log("Coldest day:", days[temps.indexOf(Math.min(...temps))], Math.min(...temps) + "°");
console.log("Average:", (temps.reduce((a, b) => a + b, 0) / temps.length).toFixed(1) + "°");
showCanvas("Weekly temperature line chart", canvas);
// ===== Helper below is not part of the lesson =====
function showCanvas(label, source) {
const copy = document.createElement("canvas");
copy.width = source.width;
copy.height = source.height;
copy.getContext("2d").drawImage(source, 0, 0);
copy.style.maxWidth = "100%";
setTimeout(() => {
const box = document.createElement("div");
box.className = "demo-canvas-box";
box.innerHTML = "<p class='demo-canvas-label'>" + label + "</p>";
box.appendChild(copy);
document.getElementById("previewContent").appendChild(box);
}, 0);
}
Pie Chart
A pie chart splits a circle into slices whose angles are proportional to the values. The whole circle is 2 x pi radians, so each slice gets (value / total) of that.
What this code does: Draws a pie chart of the market share of four products. Each value is converted to a share of the total, then to an angle, and each slice is drawn as a wedge starting where the previous one ended. A legend with colour squares and percentages sits to the right of the pie. The text output lists each product’s share and the angle of its slice in degrees; the angles add up to 360.
const canvas = document.createElement("canvas");
canvas.width = 400;
canvas.height = 300;
const ctx = canvas.getContext("2d");
// White background so the chart is readable (and looks right if you export it)
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, 400, 300);
const slices = [
{ label: "Product A", value: 35, color: "#3b82f6" },
{ label: "Product B", value: 25, color: "#22c55e" },
{ label: "Product C", value: 20, color: "#eab308" },
{ label: "Product D", value: 20, color: "#ef4444" }
];
const centerX = 120;
const centerY = 150;
const radius = 100;
const total = slices.reduce((sum, s) => sum + s.value, 0);
let startAngle = -Math.PI / 2; // start from the top (12 o'clock)
slices.forEach((slice, i) => {
const sliceAngle = (slice.value / total) * Math.PI * 2;
const endAngle = startAngle + sliceAngle;
// The slice: from the centre, along the arc, back to the centre
ctx.beginPath();
ctx.moveTo(centerX, centerY);
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
ctx.closePath();
ctx.fillStyle = slice.color;
ctx.fill();
// White separator line between slices
ctx.strokeStyle = "#ffffff";
ctx.lineWidth = 2;
ctx.stroke();
// Legend entry on the right
ctx.fillStyle = slice.color;
ctx.fillRect(250, 90 + i * 30, 16, 16);
ctx.fillStyle = "#1e293b";
ctx.font = "14px Arial";
ctx.textAlign = "left";
ctx.fillText(slice.label + " (" + slice.value + "%)", 274, 103 + i * 30);
console.log(slice.label + ": " + (slice.value / total * 100).toFixed(0) + "% of the circle = " + (sliceAngle * 180 / Math.PI).toFixed(0) + " degrees");
startAngle = endAngle;
});
showCanvas("Market share pie chart", canvas);
// ===== Helper below is not part of the lesson =====
function showCanvas(label, source) {
const copy = document.createElement("canvas");
copy.width = source.width;
copy.height = source.height;
copy.getContext("2d").drawImage(source, 0, 0);
copy.style.maxWidth = "100%";
setTimeout(() => {
const box = document.createElement("div");
box.className = "demo-canvas-box";
box.innerHTML = "<p class='demo-canvas-label'>" + label + "</p>";
box.appendChild(copy);
document.getElementById("previewContent").appendChild(box);
}, 0);
}
Grouped Bar Chart
A grouped bar chart puts two or more bars side by side for each category, so you can compare series (here, two years) within every category.
What this code does: Draws a grouped bar chart comparing 2024 and 2025 for four quarters. For every quarter there are two bars (blue for 2024, orange for 2025), each with its value above it, and the quarter name is centred under the pair. A legend in the top-right corner explains the colours. The text output lists the change from 2024 to 2025 for each quarter.
const canvas = document.createElement("canvas");
canvas.width = 500;
canvas.height = 300;
const ctx = canvas.getContext("2d");
// White background so the chart is readable (and looks right if you export it)
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, 500, 300);
const categories = ["Q1", "Q2", "Q3", "Q4"];
const year2024 = [40, 55, 70, 60];
const year2025 = [50, 65, 80, 75];
const barWidth = 25;
const groupGap = 80;
const startX = 60;
const baseY = 250;
const maxValue = 100;
const chartHeight = 200;
// Horizontal axis line
ctx.strokeStyle = "#94a3b8";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(startX - 10, baseY);
ctx.lineTo(370, baseY);
ctx.stroke();
categories.forEach((cat, i) => {
const x = startX + i * groupGap;
// 2024 bar
const h1 = (year2024[i] / maxValue) * chartHeight;
ctx.fillStyle = "#3b82f6";
ctx.fillRect(x, baseY - h1, barWidth, h1);
ctx.fillStyle = "#1e293b";
ctx.font = "12px Arial";
ctx.textAlign = "center";
ctx.fillText(year2024[i], x + barWidth / 2, baseY - h1 - 5);
// 2025 bar, drawn 8px to the right of the first
const h2 = (year2025[i] / maxValue) * chartHeight;
ctx.fillStyle = "#f97316";
ctx.fillRect(x + barWidth + 8, baseY - h2, barWidth, h2);
ctx.fillStyle = "#1e293b";
ctx.fillText(year2025[i], x + barWidth + 8 + barWidth / 2, baseY - h2 - 5);
// Category label, centered under the pair of bars
ctx.font = "14px Arial";
ctx.fillText(cat, x + (barWidth * 2 + 8) / 2, baseY + 20);
console.log(cat + ": " + year2024[i] + " -> " + year2025[i] + " (+" + (year2025[i] - year2024[i]) + ")");
});
// Legend
ctx.fillStyle = "#3b82f6";
ctx.fillRect(400, 20, 14, 14);
ctx.fillStyle = "#f97316";
ctx.fillRect(400, 42, 14, 14);
ctx.fillStyle = "#1e293b";
ctx.font = "13px Arial";
ctx.textAlign = "left";
ctx.fillText("2024", 422, 32);
ctx.fillText("2025", 422, 54);
showCanvas("Grouped bar chart: 2024 vs 2025", canvas);
// ===== Helper below is not part of the lesson =====
function showCanvas(label, source) {
const copy = document.createElement("canvas");
copy.width = source.width;
copy.height = source.height;
copy.getContext("2d").drawImage(source, 0, 0);
copy.style.maxWidth = "100%";
setTimeout(() => {
const box = document.createElement("div");
box.className = "demo-canvas-box";
box.innerHTML = "<p class='demo-canvas-label'>" + label + "</p>";
box.appendChild(copy);
document.getElementById("previewContent").appendChild(box);
}, 0);
}
Practice: Horizontal Bar Chart
A horizontal bar chart swaps the axes: bars grow to the right instead of upward, which leaves room for long labels. The same scaling idea applies, this time to the bar width.
What this code does: Draws a horizontal bar chart of the popularity of five programming languages. Each value (0 to 100) is scaled to a bar width of up to 320 pixels. The language name is right-aligned to the left of the bars, a vertical axis marks where the bars start, and the percentage is written at the end of each bar. The text output lists each bar’s width in pixels. Try changing a value or adding a language to the array.
const canvas = document.createElement("canvas");
canvas.width = 500;
canvas.height = 320;
const ctx = canvas.getContext("2d");
// White background so the chart is readable (and looks right if you export it)
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, 500, 320);
const languages = [
{ name: "JavaScript", value: 92 },
{ name: "Python", value: 85 },
{ name: "Java", value: 70 },
{ name: "C++", value: 55 },
{ name: "Ruby", value: 30 }
];
const maxValue = 100;
const barHeight = 28;
const gap = 20;
const startY = 40;
const maxBarWidth = 320;
// Vertical axis line where the bars start
ctx.strokeStyle = "#94a3b8";
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(140, startY - 10);
ctx.lineTo(140, 300);
ctx.stroke();
languages.forEach((lang, i) => {
const y = startY + i * (barHeight + gap);
const barWidth = (lang.value / maxValue) * maxBarWidth;
// Bar
ctx.fillStyle = "#8b5cf6";
ctx.fillRect(140, y, barWidth, barHeight);
// Language name
ctx.fillStyle = "#1e293b";
ctx.font = "14px Arial";
ctx.textAlign = "right";
ctx.fillText(lang.name, 130, y + 19);
// Value
ctx.textAlign = "left";
ctx.fillText(lang.value + "%", 150 + barWidth, y + 19);
console.log(lang.name + ": " + lang.value + "% -> bar width " + barWidth.toFixed(0) + "px");
});
showCanvas("Programming language popularity", canvas);
// ===== Helper below is not part of the lesson =====
function showCanvas(label, source) {
const copy = document.createElement("canvas");
copy.width = source.width;
copy.height = source.height;
copy.getContext("2d").drawImage(source, 0, 0);
copy.style.maxWidth = "100%";
setTimeout(() => {
const box = document.createElement("div");
box.className = "demo-canvas-box";
box.innerHTML = "<p class='demo-canvas-label'>" + label + "</p>";
box.appendChild(copy);
document.getElementById("previewContent").appendChild(box);
}, 0);
}
Quick Summary
- Use
fillRectfor bar charts. - Use
lineTo+arcfor line charts. - Use
arcwith calculated angles for pie charts. - Always scale your data relative to the canvas size:
(value / maxValue) * chartHeight. - The canvas y axis points downward, so a bar’s top is
baseY - barHeight. - Add labels with
fillTextfor better readability, and a legend when there is more than one series.
Tip: Click “Test My Code” and compare the numbers in the text output with the chart below it. Change a value in the data array, run it again, and watch the matching bar, point or slice change.
🧪 Test Your JavaScript Code
Edit the code on the left and click “Run Code” to see the console output on the right.
Click “Run Code” to see the output here.
Code Explanation
The complete example stays visible. Use Previous and Next to move through its logical blocks.
Select Next to begin exploring the code.