Canvas Coordinates

Canvas positions everything using a coordinate system where the origin (0,0) sits at the top-left corner, x grows rightward, and y grows downward.

The Canvas Coordinate System

Every drawing command on canvas takes coordinates measured in pixels from the canvas's origin, which is the top-left corner - not the bottom-left corner you might expect from a math class graph. The x-axis increases as you move right, exactly as you'd expect, but the y-axis increases as you move down the page. This matches how screens and images are typically addressed row by row from the top, so (0, 0) is always the top-left pixel and (canvas.width, canvas.height) is just past the bottom-right pixel.

Reading (x, y) Pairs

  • fillRect(x, y, width, height) starts its top-left corner at (x, y) and grows right and down.
  • moveTo(x, y) and lineTo(x, y) treat (x, y) as an exact pixel position, not a corner.
  • Negative x or y values are valid - they simply place drawing off the visible canvas to the left or above.
  • The bottom-right visible pixel of a canvas is at (width - 1, height - 1).

Example

<!DOCTYPE html>
<html>
<body>

<canvas id="grid" width="300" height="200" style="border:1px solid #ccc"></canvas>

<script>
  const ctx = document.getElementById("grid").getContext("2d");

  ctx.fillStyle = "crimson";
  ctx.fillRect(0, 0, 10, 10);

  ctx.fillStyle = "seagreen";
  ctx.fillRect(290, 0, 10, 10);

  ctx.fillStyle = "royalblue";
  ctx.fillRect(0, 190, 10, 10);

  ctx.fillStyle = "goldenrod";
  ctx.fillRect(290, 190, 10, 10);
</script>

</body>
</html>

Moving the Origin with translate()

ctx.translate(x, y) shifts where (0, 0) points for every drawing command that follows, without changing the canvas size. Instead of adding an offset to every coordinate in a repeating pattern, you can translate once and then draw using simple, local coordinates as if you were starting from a fresh origin.

Note: translate() is cumulative - calling it twice adds the offsets together. If you plan to draw several separate elements at different origins, wrap each one in ctx.save() / ctx.restore() so the translations don't stack up unexpectedly.

Example

<!DOCTYPE html>
<html>
<body>

<canvas id="translated" width="300" height="150"></canvas>

<script>
  const ctx = document.getElementById("translated").getContext("2d");

  ctx.fillStyle = "slateblue";
  ctx.fillRect(0, 0, 60, 60);

  ctx.save();
  ctx.translate(150, 50);
  ctx.fillStyle = "tomato";
  ctx.fillRect(0, 0, 60, 60);
  ctx.restore();
</script>

</body>
</html>

Example

<!DOCTYPE html>
<html>
<body>

<canvas id="axes" width="300" height="200"></canvas>

<script>
  const ctx = document.getElementById("axes").getContext("2d");

  ctx.strokeStyle = "#999";
  for (let x = 0; x <= 300; x += 30) {
    ctx.beginPath();
    ctx.moveTo(x, 0);
    ctx.lineTo(x, 200);
    ctx.stroke();
  }
  for (let y = 0; y <= 200; y += 30) {
    ctx.beginPath();
    ctx.moveTo(0, y);
    ctx.lineTo(300, y);
    ctx.stroke();
  }

  ctx.fillStyle = "black";
  ctx.font = "12px sans-serif";
  ctx.fillText("(0,0)", 2, 12);
  ctx.fillText("(300,200)", 230, 195);
</script>

</body>
</html>
Note: Because y increases downward, code translated from math-class formulas (like graphing y = x^2) often appears upside down on canvas. Flip the sign of your y calculation, or use ctx.translate() with ctx.scale(1, -1) to restore a bottom-up coordinate system.
ConceptBehavior
Origin (0,0)Top-left corner of the canvas
x-axisIncreases to the right
y-axisIncreases downward
Negative coordinatesValid; place drawing outside the visible area
translate(x, y)Shifts the origin for all later drawing commands

Exercise: Canvas Coordinates

Where is the coordinate origin (0,0) located on an HTML canvas by default?