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.
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.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.