Frontend Development 21 min read

Create a Rolex GMT‑MASTER Style Watch Using HTML Canvas

This tutorial walks a front‑end developer through building a fully functional Rolex‑inspired GMT‑MASTER watch with HTML, CSS and JavaScript by dividing the watch into dial, bezel and hand modules, initializing three canvas elements, defining color variables, and implementing drawing functions for the bezel, dial, hour, GMT, minute and second hands.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Create a Rolex GMT‑MASTER Style Watch Using HTML Canvas

Inspired by a desire to own a Rolex "GMT‑MASTER" but lacking the budget, the author decides to recreate the watch entirely with web technologies. The project is split into three canvas modules—dial, bezel and hands—each rendered on its own <canvas> element.

The HTML structure is minimal:

<div class="watch-box">
  <!-- Hand canvas -->
  <canvas id="watchPointer" width="1800" height="1800"></canvas>
  <!-- Dial canvas -->
  <canvas id="dial" width="1800" height="1800"></canvas>
  <!-- Bezel canvas -->
  <canvas id="bezel" width="1800" height="1800"></canvas>
</div>

In JavaScript the three canvases are selected and their 2‑D contexts obtained. A scaling factor of 4 is applied so that drawing commands work at a higher logical resolution while the displayed size stays reasonable.

const watchBox = document.querySelector('.watch-box');
const watchPointer = document.querySelector('#watchPointer');
const dial = document.querySelector('#dial');
const bezel = document.querySelector('#bezel');
const ctx = watchPointer.getContext('2d');
const dialCtx = dial.getContext('2d');
const bezelCtx = bezel.getContext('2d');
const ratio = 4;
ctx.scale(ratio, ratio);
 dialCtx.scale(ratio, ratio);
 bezelCtx.scale(ratio, ratio);

Color constants are defined for later reuse, making it easy to change the watch’s palette:

const gmtBezelRed = '#8a2811';
const blue = '#133760';
const black = '#10111e';
const white = '#fff';
const grayD = '#ddd';
const grayC = '#ccc';
const grayB = '#bbb';
const grayA = '#aaa';
const gmtPointerRed = '#aa0d0f';

The drawGmtBezel function draws the two‑tone ceramic bezel, adds a metallic outer ring, and renders the 60 minute markers, hour numbers, and the distinctive GMT window. It uses translate to move the origin to the canvas centre (225, 225) and arc for circular shapes.

function drawGmtBezel() {
  bezelCtx.save();
  bezelCtx.clearRect(0,0,1800,1800);
  bezelCtx.translate(225,225);
  // shadow settings
  bezelCtx.shadowOffsetX = 50;
  bezelCtx.shadowOffsetY = 50;
  bezelCtx.shadowColor = 'rgba(0,0,0,0.5)';
  bezelCtx.shadowBlur = 100;
  const drawCeramicCircle = (ctx, begin, end, color) => {
    ctx.beginPath();
    ctx.lineWidth = 26.5;
    ctx.arc(0,0,113.25,begin,end);
    ctx.strokeStyle = color;
    ctx.stroke();
    ctx.closePath();
  };
  drawCeramicCircle(bezelCtx, Math.PI, 2*Math.PI, blue); // upper half
  drawCeramicCircle(bezelCtx, 0, Math.PI, gmtBezelRed); // lower half
  // outer metal ring
  bezelCtx.beginPath();
  bezelCtx.lineWidth = 6;
  bezelCtx.arc(0,0,129.5,0,2*Math.PI);
  bezelCtx.strokeStyle = grayD;
  bezelCtx.stroke();
  bezelCtx.closePath();
  // minute markers … (loop omitted for brevity)
  bezelCtx.restore();
}

The drawDial function creates the watch face: a dark outer disc, the Rolex logo, brand text, the date window, and the hour/minute tick marks. Text is drawn with fillText after rotating the canvas for each marker.

function drawDial() {
  dialCtx.save();
  dialCtx.clearRect(0,0,1800,1800);
  dialCtx.translate(225,225);
  // outer circle
  dialCtx.beginPath();
  dialCtx.arc(0,0,100,0,2*Math.PI);
  dialCtx.strokeStyle = grayC;
  dialCtx.stroke();
  // black inner disc
  dialCtx.beginPath();
  dialCtx.arc(0,0,53,0,2*Math.PI);
  dialCtx.fillStyle = black;
  dialCtx.strokeStyle = black;
  dialCtx.lineWidth = 94;
  dialCtx.stroke();
  // logo and text (omitted for brevity)
  // hour/minute markers loop …
  dialCtx.restore();
}

The drawWatchPointer routine renders the hour hand, the iconic "Mercedes"‑style tip, the GMT hand, the minute hand and the second hand. It first draws the date window, then rotates the canvas according to the current time obtained via new Date() . Each hand is built from rectangles, arcs and custom paths, with shadows for depth.

function drawWatchPointer() {
  ctx.save();
  ctx.clearRect(0,0,1800,1800);
  ctx.translate(225,225);
  const time = new Date();
  const hour = time.getHours() % 12;
  const min = time.getMinutes();
  const second = time.getSeconds();
  const millisecond = time.getMilliseconds();
  // date window
  ctx.fillStyle = '#000';
  ctx.font = 'bold 16px AppleGothic';
  const day = time.getDate();
  ctx.fillText(day, day<15?63.5:58, 6);
  // hour hand
  ctx.rotate(((2*Math.PI)/12)*hour + ((2*Math.PI)/12)*(min/60) - Math.PI/2));
  ctx.fillRect(0,-4,40,8);
  // ... other hand drawing code omitted for brevity
  ctx.restore();
}

After all drawing functions are defined, the images for the Rolex logo and a small brand mark are loaded. Once both images finish loading, drawDial() , drawGmtBezel() and a setInterval that calls drawWatchPointer every 100 ms are started, producing a live, animated watch that shows the current time, GMT offset and date.

This example demonstrates how canvas can be used for complex, animated UI components, teaches coordinate transformations, path management and time‑based animation, and provides a complete, copy‑and‑pasteable source for further experimentation.

JavaScriptCSSHTML CanvasFrontend TutorialCanvas DrawingWatch Design
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.