HTML Canvas Intro
The <canvas> element gives you a blank rectangular area on a web page that can be drawn on pixel by pixel. On its own, plain HTML markup, a <canvas> does nothing visible — it is simply an empty drawing surface waiting for a program to paint on it. This lesson explains the element itself: what it is, how the browser treats it, its attributes, and how to mark it up correctly and accessibly. The actual drawing commands (lines, shapes, images, animation) are part of JavaScript and are covered in the JavaScript course — here we focus entirely on the HTML side.
Overview / How it works
The <canvas> element was introduced in HTML5 as a way to let pages render bitmap graphics directly in the browser, without plugins like Flash or Java applets. When the HTML parser encounters a <canvas> tag, it creates a normal DOM node for it, just like a <div> or a <p>. That node occupies space in the page layout as an inline-replaced element (similar in box behavior to an <img>), but by default it renders as a completely transparent, borderless rectangle. If you don’t specify a size, the browser gives it a default drawing surface of 300 pixels wide by 150 pixels tall.
Here is the key distinction that trips up many beginners: a <canvas> element by itself is just an empty rectangle in the DOM. Nothing appears inside it until JavaScript retrieves a "rendering context" from it (for example a 2D context or a WebGL context) and issues drawing commands. Because the canvas has no built-in shapes, text, or state of its own that the DOM can inspect, it is called an immediate mode graphics surface: once something is drawn, the browser doesn’t remember it as a scene graph of individual pixels or shapes — it just remembers the final bitmap. This is very different from SVG, which is a retained mode vector format: every shape in an SVG image is its own DOM node that you can select, style, and update individually. Canvas is better suited to pixel-level work such as game rendering, image manipulation, and data-driven charts that redraw frequently; SVG is better suited to graphics that need to stay sharp at any zoom level, respond to CSS, or be individually addressable.
Because a browser that doesn’t support canvas (or a user with JavaScript disabled, or many screen readers) cannot show anything inside it, the HTML specification allows you to put fallback content between the opening and closing <canvas> tags. That fallback content is ignored and not rendered by browsers that do support canvas and have run the drawing script; it is only shown to browsers/assistive technology that cannot process the canvas normally. This makes the fallback content one of the few genuinely important pieces of markup you write directly inside a <canvas> tag.
Syntax
<canvas id="myCanvas" width="400" height="200">
Fallback content for browsers or assistive technology
that cannot display the canvas.
</canvas>
| Attribute | Purpose |
|---|---|
id |
A unique identifier so JavaScript can find the element with document.getElementById() and start drawing on it. |
width |
The width of the canvas’s internal drawing surface, in pixels. Defaults to 300 if omitted. |
height |
The height of the canvas’s internal drawing surface, in pixels. Defaults to 150 if omitted. |
class |
Standard global attribute; used to apply CSS layout/border styling from a stylesheet (not covered in this HTML-only lesson). |
| (fallback content) | Any HTML placed between the tags, shown only when canvas cannot be rendered. |
Note that <canvas> is not a void/self-closing element like <img> or <br>. It always requires an explicit closing tag, precisely because it is allowed to contain fallback content.
Examples
Example 1: A basic canvas with fallback content
<canvas id="welcomeCanvas" width="300" height="150">
<p>Your browser does not support the canvas element.</p>
</canvas>
Result: In a modern browser with JavaScript enabled, this renders as an invisible 300×150 pixel box — nothing is visually distinguishable on the page because no drawing script has run yet. In a browser without canvas support, or when read by a screen reader that doesn’t process canvas content, the fallback paragraph "Your browser does not support the canvas element." is shown/announced instead.
This example demonstrates the minimum correct markup: a unique id so a script can target it later, explicit width and height, and meaningful fallback content rather than leaving the element empty.
Example 2: Multiple canvases with different sizes
<section>
<h3>Sales Chart</h3>
<canvas id="salesChart" width="500" height="250">
<p>Chart unavailable: quarterly sales rose 12% year over year.</p>
</canvas>
<h3>Mini Sparkline</h3>
<canvas id="sparkline" width="120" height="30">
<p>Trend unavailable.</p>
</canvas>
</section>
Result: The page shows a heading and an empty 500×250 pixel drawing area for the sales chart, followed by another heading and a smaller empty 120×30 pixel area for the sparkline. Each is a separate, independently sized drawing surface with its own unique id. Until a script draws into them, both areas are blank and take up their reserved rectangular space in the layout.
This shows a realistic pattern: a page can contain several canvases, each representing a different piece of visual data, each needing a unique id and meaningful fallback text describing what the graphic would otherwise convey (important for accessibility and for search engines, which cannot read pixels).
Example 3: A canvas inside a full page, drawn by an external script
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Canvas Demo Page</title>
</head>
<body>
<h1>Drawing Demo</h1>
<canvas id="demoCanvas" width="400" height="200">
<p>A red rectangle would appear here if your browser supported canvas and JavaScript.</p>
</canvas>
<script src="draw.js"></script>
</body>
</html>
Result: The browser parses and displays an empty page shell with a heading "Drawing Demo" and a 400×200 pixel canvas below it. If draw.js loads successfully and uses the 2D drawing API on #demoCanvas, shapes would then appear inside that rectangle; the HTML itself never contains the drawing instructions.
This full-document example shows where <canvas> fits into an ordinary page: it is placed in the body flow like any block-level content, referenced by id, and paired with a separate script file that does the actual drawing — that script’s contents are a JavaScript-course topic, not an HTML one.
How it works step by step / Under the hood
- The HTML parser reaches the
<canvas>start tag and creates aHTMLCanvasElementDOM node, using the default 300×150 size unlesswidth/heightattributes override it. - Anything between the opening and closing tags is parsed as normal child nodes and attached to the canvas element in the DOM, exactly like children of a
<div>. - The browser paints the canvas node into the page layout as an empty, transparent rectangle of the specified pixel dimensions. No visual content is generated from the markup alone.
- If canvas is supported and enabled, the browser does not render the fallback children visually (they still exist in the DOM, but are suppressed from display) once a rendering context is requested.
- Later, when a script calls something like
getContext("2d")on the element, the browser hands back an API object; any drawing calls made through that object modify the underlying pixel buffer of that specific canvas, which the browser then paints on screen. - If no script ever runs (JS disabled, blocked, or the browser predates canvas support), the fallback content inside the tags is what a user or assistive technology actually sees.
Common Mistakes
Mistake 1: Self-closing the canvas tag
<canvas id="chart" width="300" height="150" />
This is wrong because <canvas> is not a void element. HTML does not treat a trailing slash on a non-void tag as a real self-close; the parser keeps the canvas open and any following markup gets nested inside it as fallback content, which is rarely what you want.
<canvas id="chart" width="300" height="150"></canvas>
The corrected version uses an explicit closing tag, even when there is no fallback content to include.
Mistake 2: Leaving the canvas with no fallback content
<canvas id="profileChart" width="400" height="200"></canvas>
This is technically valid markup, but it is a design mistake: if JavaScript fails to load, is blocked, or is being read by a screen reader that skips canvas, the user sees or hears nothing at all — not even a hint that a chart was supposed to be there.
<canvas id="profileChart" width="400" height="200">
<p>Profile completeness chart: 80% complete.</p>
</canvas>
The corrected version supplies a short text description of what the chart shows, so the information is never fully lost.
Mistake 3: Confusing the width/height attributes with CSS sizing
A related but subtler mistake (no code sample needed to see the problem) is styling a canvas’s on-page size purely through CSS width and height rules while leaving the HTML width/height attributes at their defaults. The HTML attributes define the number of pixels in the actual drawing buffer; CSS sizing only stretches or shrinks that buffer visually, the same way scaling up a small photo makes it blurry. A canvas whose HTML attributes say 300×150 but is stretched with CSS to 900×450 will look pixelated, because the browser is scaling a small bitmap up rather than drawing at the larger resolution. Always set the HTML width/height attributes to the resolution you actually want drawn.
Best Practices
- Always set explicit
widthandheightattributes rather than relying on the 300×150 default, so the reserved layout space matches your design. - Always provide meaningful fallback content describing what the canvas would show, for browsers without canvas support and for assistive technology.
- Give every canvas a unique, descriptive
idso scripts can target the correct element without ambiguity. - Use the HTML
width/heightattributes to control drawing resolution, and reserve CSS only for layout positioning, not for stretching the pixel buffer. - Prefer SVG instead of canvas when the graphic is simple, needs to scale crisply at any zoom level, or needs individual shapes to be selectable/stylable via CSS.
- Add an
aria-labelor nearby visible caption describing non-decorative canvas content, since canvas pixels carry no semantic meaning for assistive technology or search engines on their own. - Keep the number of canvases on a single page reasonable; each one holds its own pixel buffer in memory, which can add up on complex pages.
Practice Exercises
- Create a
<canvas>element withid="exercise1", a width of 350 pixels and a height of 175 pixels, including a fallback paragraph describing a bar chart of monthly rainfall. - Add two more canvases to the same page, each with a unique
idand a different size, and give each one a preceding heading describing what it represents. - Take the self-closed canvas example from the Common Mistakes section (
<canvas id="chart" width="300" height="150" />) and rewrite it as valid, properly closed HTML with appropriate fallback text.
Summary
- The
<canvas>element defines a blank, pixel-based drawing surface in the page; it renders as an invisible rectangle until JavaScript draws into it. - Its
widthandheightattributes set the actual drawing resolution and default to 300×150 pixels if omitted. - Canvas content between the opening and closing tags is fallback content, shown only when canvas cannot be used, so it should always be included.
- Canvas is not a void element and must always have a closing tag, even when empty.
- Canvas is immediate-mode bitmap graphics; SVG is retained-mode vector graphics with individually addressable DOM nodes — choose based on whether you need scalability and per-shape styling (SVG) or raw pixel control and frequent redraws (canvas).
- The actual drawing logic (shapes, images, animation) belongs to JavaScript, covered separately in the JavaScript course.
