Graphics Drawing the Clock Hands

This lesson adds hour, minute, and second hands to the clock face by reading the current time from a Date object and converting it into rotation angles.

From a Static Face to Moving Hands

With the face and ticks in place, the clock still shows no time at all. Hands are just lines drawn from the center outward, rotated to the correct angle for the current hour, minute, and second. The interesting part is not drawing the lines, it is computing the angles correctly so the hands land in the right place and move smoothly instead of jumping.

  • Read the current hour, minute, and second from a Date object
  • Convert each unit into a rotation angle in radians
  • Make sure the hour hand also shifts gradually as minutes pass, instead of snapping at the top of every hour
  • Give each hand its own length and line width so hour, minute, and second are visually distinct

Example

<!DOCTYPE html>
<html>
<head>
</head>
<body>

<canvas id="clock" width="300" height="300"></canvas>

<script>
function getHandAngles() {
  const now = new Date();
  const hours = now.getHours() % 12;
  const minutes = now.getMinutes();
  const seconds = now.getSeconds();

  const secondAngle = seconds * (Math.PI / 30);
  const minuteAngle = minutes * (Math.PI / 30) + secondAngle / 60;
  const hourAngle = hours * (Math.PI / 6) + minuteAngle / 12;

  return { hourAngle, minuteAngle, secondAngle };
}
</script>

</body>
</html>

Each second is 1/60 of a full turn, so multiplying seconds by PI / 30 (which is 2*PI / 60) gives the second hand's angle. The minute hand works the same way, but minuteAngle also adds secondAngle / 60, which folds in how far through the current minute the seconds have gotten, so the minute hand creeps forward continuously instead of jumping once every 60 seconds.

Note: The hour hand needs the same treatment, one level up: hourAngle adds minuteAngle / 12, so the hour hand drifts smoothly between hour marks as minutes pass, instead of staying glued to the 3 until the clock strikes 4:00 exactly. Skipping this term is the most common bug in a canvas clock, the hour hand looks stuck for 59 minutes and then jumps.

Two Ways to Point a Hand at an Angle

Because the ticks were drawn by rotating the context and drawing a vertical line upward, the hands can reuse the exact same trick: rotate to the hand's angle, draw a line from the origin straight up (negative y), then undo the rotation. Straight up here is already correct for a clock, because when angle is 0 the hand points along the local negative y-axis, visually 12 o'clock, with no extra offset needed.

Example

<!DOCTYPE html>
<html>
<head>
</head>
<body>

<canvas id="clock" width="300" height="300"></canvas>

<script>
function drawHand(ctx, angle, length, width) {
  ctx.save();
  ctx.rotate(angle);
  ctx.beginPath();
  ctx.lineWidth = width;
  ctx.lineCap = 'round';
  ctx.moveTo(0, 0);
  ctx.lineTo(0, -length);
  ctx.stroke();
  ctx.restore();
}
</script>

</body>
</html>

If you instead compute a hand's endpoint directly with cos() and sin(), without ctx.rotate(), the offset shows up explicitly. Canvas measures angles the way trigonometry normally does: 0 radians points along the positive x-axis, toward 3 o'clock, with angles increasing clockwise because canvas y grows downward. A clock's 0 position is 12 o'clock, straight up, which is a quarter turn counter-clockwise from 3 o'clock, so you subtract Math.PI / 2 before taking the cosine and sine.

Example

<!DOCTYPE html>
<html>
<head>
</head>
<body>

<canvas id="clock" width="300" height="300"></canvas>

<script>
function handTip(angleFromTwelve, length) {
  // Canvas/math angle 0 points right (3 o'clock).
  // Subtracting Math.PI / 2 rotates the reference so 0 points up (12 o'clock).
  const canvasAngle = angleFromTwelve - Math.PI / 2;
  return {
    x: length * Math.cos(canvasAngle),
    y: length * Math.sin(canvasAngle)
  };
}
</script>

</body>
</html>
HandLength (fraction of clockRadius)Width (fraction of clockRadius)Typical color
Hour0.50.07Same as tick marks, e.g. #333
Minute0.80.05Same as tick marks, e.g. #333
Second0.90.02An accent color, e.g. red