Let's Build a Game Engine in HTML
Chapter 1
1 / 9

Canvas Foundation

Your first surface — the HTML canvas element

Chapter goal

Create a full document with a canvas, grab a 2D drawing context, and paint a solid world so the browser becomes your game screen.

HTML document shell
canvas element
getContext('2d')
fillRect & clearRect

How to do it

  1. 1

    Scaffold a page

    A game engine in pure HTML starts as a normal webpage: doctype, head, body. Inside the body you place a single <canvas> — the pixel surface everything draws onto.

  2. 2

    Get the 2D context

    Call canvas.getContext('2d') once. That context object (ctx) is the API for clearing pixels, drawing shapes, text, and later images.

  3. 3

    Paint the world

    fillStyle sets the color; fillRect(x, y, w, h) stamps a rectangle. Coordinates start at the top-left (0,0) and increase right and down.

Live lab

Solution code updates with sliders. Tinker is your sandbox. Play runs whichever document you launch.

Live settings

Every slider injects into the engine constants for this chapter.

Type-along practice
Photo layout
Canvas Foundation
1 / 65 lines

The chapter engine script is prewritten below. Type it line by line — each line explains why it belongs there. Matching a line advances automatically; Enter re-checks if you are stuck.

Prewritten engine scriptmistakes 0
1(function () {
2 "use strict";
3 // Single boot — if this document is re-parsed, abandon prior work.
4 if (window.__engineBooted) return;
5 window.__engineBooted = true;
6 let alive = true;
7 window.addEventListener("pagehide", function () { alive = false; }, { once: true });
8 const raf = window.requestAnimationFrame.bind(window);
9 window.requestAnimationFrame = function (cb) {
10 return raf(function (ts) {
11 if (!alive) return;
12 try { cb(ts); } catch (err) { console.error(err); alive = false; }
13 });
14 };
15
16const canvas = document.getElementById('c');
17const ctx = canvas.getContext('2d');
18const W = canvas.width, H = canvas.height;
19
20const BG_R = 11;
21const BG_G = 16;
22const BG_B = 24;
23const GRID = 32;
24const ACCENT = 55 / 100;
25
26function draw() {
27 ctx.fillStyle = `rgb(${BG_R},${BG_G},${BG_B})`;
28 ctx.fillRect(0, 0, W, H);
29
30 ctx.strokeStyle = `rgba(94,234,212,${0.08 + ACCENT * 0.12})`;
31 ctx.lineWidth = 1;
32 for (let x = 0; x <= W; x += GRID) {
33 ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, H); ctx.stroke();
34 }
35 for (let y = 0; y <= H; y += GRID) {
36 ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(W, y); ctx.stroke();
37 }
38
39 // Horizon wash
40 const g = ctx.createLinearGradient(0, H * 0.4, 0, H);
41 g.addColorStop(0, 'transparent');
42 g.addColorStop(1, `rgba(94,234,212,${0.05 + ACCENT * 0.15})`);
43 ctx.fillStyle = g;
44 ctx.fillRect(0, 0, W, H);
45
46 // Title plate
47 ctx.fillStyle = `rgba(94,234,212,${0.12 + ACCENT * 0.25})`;
48 roundRect(ctx, W/2 - 140, H/2 - 36, 280, 72, 14);
49 ctx.fill();
50 ctx.strokeStyle = `rgba(94,234,212,${0.35 + ACCENT * 0.4})`;
51 ctx.stroke();
52
53 ctx.fillStyle = '#e8edf5';
54 ctx.font = '600 18px system-ui,sans-serif';
55 ctx.textAlign = 'center';
56 ctx.fillText('Engine Surface Online', W/2, H/2 - 4);
57 ctx.fillStyle = '#8b97ab';
58 ctx.font = '12px ui-monospace,monospace';
59 ctx.fillText(W + '×' + H + ' canvas · 2D context', W/2, H/2 + 18);
60
61 document.getElementById('hud').textContent = 'Chapter 1 · static frame';
62}
63
64function roundRect(ctx, x, y, w, h, r) {
65 ctx.beginPath();
66 ctx.moveTo(x+r, y);
67 ctx.arcTo(x+w, y, x+w, y+h, r);
68 ctx.arcTo(x+w, y+h, x, y+h, r);
69 ctx.arcTo(x, y+h, x, y, r);
70 ctx.arcTo(x, y, x+w, y, r);
71 ctx.closePath();
72}
73
74draw();
75
76})();
Your lineline 1
(function () {

Static scene — use sliders to restyle the canvas world. · Sliders update the play window after a short pause.

Chapter solution
Reference
Live-bound HTML
Play window
Live