Today we are going to build a stopwatch with HTML, CSS and JavaScript. Basically, this is a great project if your are learning front-end development. This project is a great exercise in combining HTML for structure, CSS for styling, and JavaScript for functionality. A stopwatch is very useful tool for measuring time; they are symbols of precision, efficiency, and productivity. If you’re timing a race, tracking a cooking session, or managing tasks, having a stopwatch can be incredibly useful.

In this guide, you’ll not only learnt to build a fully functional stopwatch but also a deeper understanding of how these technologies i.e HTML, CSS and JavaScript, work together to create dynamic web experiences.

Stopwatch : Beginner JavaScript Project

HTML Structure:

First of all, create a index.html file in which we’ll define the structure of our stopwatch interface, including the display area and control buttons. Let’s break down each component:

<div class="stopwatch">
	<div class="buttons">
		<div class="button button-left" title="Reset">
			<div></div>
		</div>
		<div class="button button-right" title="Start / Stop">
			<div></div>
		</div>
	</div>
	<div class="rim">
		<div class="clock-container">
			<div class="clock minutes-clock">
				<div class="lines">
					<div class="line" style="--line-idx:0"></div>
					<div class="line" style="--line-idx:1"></div>
					<div class="line" style="--line-idx:2"></div>
					<div class="line" style="--line-idx:3"></div>
					<div class="line" style="--line-idx:4"></div>
					<div class="line" style="--line-idx:5"></div>
					<div class="line" style="--line-idx:6"></div>
					<div class="line" style="--line-idx:7"></div>
					<div class="line" style="--line-idx:8"></div>
					<div class="line" style="--line-idx:9"></div>
					<div class="line" style="--line-idx:10"></div>
					<div class="line" style="--line-idx:11"></div>
					<div class="line" style="--line-idx:12"></div>
					<div class="line" style="--line-idx:13"></div>
					<div class="line" style="--line-idx:14"></div>
					<div class="line" style="--line-idx:15"></div>
					<div class="line" style="--line-idx:16"></div>
					<div class="line" style="--line-idx:17"></div>
					<div class="line" style="--line-idx:18"></div>
					<div class="line" style="--line-idx:19"></div>
					<div class="line" style="--line-idx:20"></div>
					<div class="line" style="--line-idx:21"></div>
					<div class="line" style="--line-idx:22"></div>
					<div class="line" style="--line-idx:23"></div>
					<div class="line" style="--line-idx:24"></div>
					<div class="line" style="--line-idx:25"></div>
					<div class="line" style="--line-idx:26"></div>
					<div class="line" style="--line-idx:27"></div>
					<div class="line" style="--line-idx:28"></div>
					<div class="line" style="--line-idx:29"></div>
				</div>
				<div class="numbers">
					<div class="number" style="--number-idx:0">
						<div>60</div>
					</div>
					<div class="number" style="--number-idx:1">
						<div>5</div>
					</div>
					<div class="number" style="--number-idx:2">
						<div>10</div>
					</div>
					<div class="number" style="--number-idx:3">
						<div>15</div>
					</div>
					<div class="number" style="--number-idx:4">
						<div>20</div>
					</div>
					<div class="number" style="--number-idx:5">
						<div>25</div>
					</div>
					<div class="number" style="--number-idx:6">
						<div>30</div>
					</div>
					<div class="number" style="--number-idx:7">
						<div>35</div>
					</div>
					<div class="number" style="--number-idx:8">
						<div>40</div>
					</div>
					<div class="number" style="--number-idx:9">
						<div>45</div>
					</div>
					<div class="number" style="--number-idx:10">
						<div>50</div>
					</div>
					<div class="number" style="--number-idx:11">
						<div>55</div>
					</div>
				</div>
				<div class="handle">
					<div class="pointer"></div>
					<div class="circle">
						<div></div>
					</div>
				</div>
			</div>
			<div class="clock seconds-clock">
				<div class="lines">
					<div class="line" style="--line-idx:0"></div>
					<div class="line" style="--line-idx:1"></div>
					<div class="line" style="--line-idx:2"></div>
					<div class="line" style="--line-idx:3"></div>
					<div class="line" style="--line-idx:4"></div>
					<div class="line" style="--line-idx:5"></div>
					<div class="line" style="--line-idx:6"></div>
					<div class="line" style="--line-idx:7"></div>
					<div class="line" style="--line-idx:8"></div>
					<div class="line" style="--line-idx:9"></div>
					<div class="line" style="--line-idx:10"></div>
					<div class="line" style="--line-idx:11"></div>
					<div class="line" style="--line-idx:12"></div>
					<div class="line" style="--line-idx:13"></div>
					<div class="line" style="--line-idx:14"></div>
					<div class="line" style="--line-idx:15"></div>
					<div class="line" style="--line-idx:16"></div>
					<div class="line" style="--line-idx:17"></div>
					<div class="line" style="--line-idx:18"></div>
					<div class="line" style="--line-idx:19"></div>
					<div class="line" style="--line-idx:20"></div>
					<div class="line" style="--line-idx:21"></div>
					<div class="line" style="--line-idx:22"></div>
					<div class="line" style="--line-idx:23"></div>
					<div class="line" style="--line-idx:24"></div>
					<div class="line" style="--line-idx:25"></div>
					<div class="line" style="--line-idx:26"></div>
					<div class="line" style="--line-idx:27"></div>
					<div class="line" style="--line-idx:28"></div>
					<div class="line" style="--line-idx:29"></div>
				</div>
				<div class="numbers">
					<div class="number" style="--number-idx:0">
						<div>60</div>
					</div>
					<div class="number" style="--number-idx:1">
						<div>5</div>
					</div>
					<div class="number" style="--number-idx:2">
						<div>10</div>
					</div>
					<div class="number" style="--number-idx:3">
						<div>15</div>
					</div>
					<div class="number" style="--number-idx:4">
						<div>20</div>
					</div>
					<div class="number" style="--number-idx:5">
						<div>25</div>
					</div>
					<div class="number" style="--number-idx:6">
						<div>30</div>
					</div>
					<div class="number" style="--number-idx:7">
						<div>35</div>
					</div>
					<div class="number" style="--number-idx:8">
						<div>40</div>
					</div>
					<div class="number" style="--number-idx:9">
						<div>45</div>
					</div>
					<div class="number" style="--number-idx:10">
						<div>50</div>
					</div>
					<div class="number" style="--number-idx:11">
						<div>55</div>
					</div>
				</div>
				<div class="handle">
					<div class="pointer"></div>
					<div class="circle">
						<div></div>
					</div>
				</div>

			</div>
			<div class="glass-overlay"></div>
		</div>
	</div>
</div>

Styling with CSS:

This CSS code provides basic styling for the stopwatch display and control buttons, including font size, colors, and spacing etc.

@import url("https://fonts.googleapis.com/css2?family=Cairo&display=swap");

*,
*::before,
*::after {
	margin: 0;
	padding: 0;
	box-sizing: border-box;
}
body {
	display: grid;
	height: 100vh;
	place-items: center;
	background: linear-gradient(to bottom right, grey, black);
}

.stopwatch {
	padding: 2em;
	font-size: 2vmin;
	font-family: sans-serif;
	filter: drop-shadow(2em 2em 1em black);
	position: relative;
	--animation-play-state: paused;
}
.rim {
	padding: 2.5em;
	background: linear-gradient(
		to bottom right,
		rgb(155, 155, 155),
		rgb(196, 196, 196)
	);
	box-shadow: inset 1em 1em 2em rgba(255, 255, 255, 255.5),
		inset -1em -1em 2em rgba(0, 0, 0, 0.8);
	border-radius: 50%;
}
.clock-container {
	border-radius: 50%;
	display: grid;
	grid-template-areas: "clock-container-center";
	border: 0em solid black;
	background: linear-gradient(to top left, rgb(200, 200, 200), white);
	box-shadow: inset 0.5em 0.5em 1em rgba(0, 0, 0, 0.8),
		inset -0.5em -0.5em 1em rgba(255, 255, 255, 0.5);
	font-family: "Cairo", sans-serif;
	line-height: 1;
	position: relative;
}

.clock {
	grid-area: clock-container-center;
	width: 30em;
	aspect-ratio: 1/1;
	border-radius: 50%;
	display: grid;
	grid-template-areas: "sub-clock-container-center";
	overflow: hidden;
}
.minutes-clock {
	font-size: 0.32em;
	justify-self: center;
	margin-top: 14%;
}
.clock > * {
	grid-area: sub-clock-container-center;
	width: 100%;
	height: 100%;
}

.clock > .lines {
	display: grid;
	grid-template-areas: "lines-center";
	place-items: center;
}
.clock > .lines > .line {
	grid-area: lines-center;
	--line-width: 0.1em;
	--line-length: 1em;
	width: var(--line-width);
	height: 100%;
	background-image: linear-gradient(
		black var(--line-length),
		transparent 0 calc(100% - var(--line-length)),
		black 0
	);
	transform: rotate(calc(360deg / 60 * var(--line-idx)));
}

.clock > .lines > div:nth-child(5n + 1) {
	--line-width: 0.25em;
	--line-length: 1.5em;
}

.clock > .numbers {
	display: grid;
	grid-template-areas: "numbers-center";
	place-items: center;
}
.clock > .numbers > .number {
	grid-area: numbers-center;
	height: 86%;
	font-size: 1.8em;
	--rotate: calc(360deg / 12 * var(--number-idx));
	transform: rotate(var(--rotate));
}
.clock > .numbers > .number > div {
	transform: rotate(calc(-1 * var(--rotate)));
}

.clock > .handle {
	display: grid;
	grid-template-areas: "handle-center";
	place-items: center;
	z-index: 2;
	filter: drop-shadow(0.1em 0.1em 0.2em rgba(0, 0, 0, 0.5));
}

.clock > .handle > .pointer {
	grid-area: handle-center;
	background-color: #ce3131;
	width: 1em;
	height: 95%;
	clip-path: polygon(55% 0%, 100% 60%, 0% 60%, 45% 0);
	--duration: 60s;
	--timingFunction: linear;
	animation: clock-anim var(--duration) var(--timingFunction) infinite;
	animation-play-state: var(--animation-play-state);
}

.minutes-clock > .handle > .pointer {
	--duration: 3600s;
	--timingFunction: steps(60, end);
}
@keyframes clock-anim {
	to {
		transform: rotate(360deg);
	}
}
.clock > .handle > .circle {
	grid-area: handle-center;
	background-color: #ce3131;
	width: 2em;
	aspect-ratio: 1/1;
	z-index: 1;
	border-radius: 50%;
	display: grid;
	place-content: center;
}
.clock > .handle > .circle > div {
	width: 1em;
	aspect-ratio: 1/1;
	border-radius: 50%;
	background: linear-gradient(
		to bottom right,
		rgb(194, 194, 194),
		rgba(255, 255, 255, 1)
	);
	box-shadow: inset 0.05em 0.05em 0.1em rgba(255, 255, 255, 0.5),
		inset -0.05em -0.05em 0.1em rgba(0, 0, 0, 0.5),
		0.1em 0.1em 0.2em rgba(0, 0, 0, 0.5);
}

.clock-container > .glass-overlay {
	grid-area: clock-container-center;
	content: "";
	width: 100%;
	height: 100%;
	border-radius: 50%;
	z-index: 2;
	/* background: blue; */
	background-image: radial-gradient(
		circle at top left,
		rgba(255, 255, 255, 0.25) 60%,
		transparent 60.9%,
		rgba(0, 0, 0, 0.01)
	);
	filter: ba;
}

.buttons {
	position: absolute;
	left: 50%;
	height: 100%;
	top: 0;
	transform: translateX(-50%);
	display: grid;
	grid-template-areas: "button-container-center";
	z-index: -1;
}
.buttons > .button {
	grid-area: button-container-center;
	transform: rotate(var(--rotate));
}

.buttons > .button-left {
	--rotate: -30deg;
}
.buttons > .button-right {
	--rotate: 30deg;
}
.buttons > .button > div {
	background: rgb(200, 200, 200);
	width: 2.5em;
	height: 2.5em;
	border-radius: 0.5em;
	--knobColor: #ce3131;
	background-image: linear-gradient(var(--knobColor), 0.5em, transparent 0.5em),
		linear-gradient(
			to right,
			transparent 0 30%,
			rgba(0, 0, 0, 0.3) 30% 70%,
			rgba(0, 0, 0, 0.5) 70% 72%,
			transparent 70% 100%
		);
	background-size: 0.3em 100%;
	background-position: center;
	box-shadow: inset -0.5em 0em 0.25em rgba(0, 0, 0, 0.25),
		inset 0.5em 0em 0.25em rgba(255, 255, 255, 0.25);

	transform: translateY(var(--translateY, 0));
	cursor: pointer;
}

.stopwatch.running {
	--animation-play-state: running;
}
.stopwatch.running .buttons > .button-right > div {
	--translateY: 0.75em;
	--knobColor: rgb(4, 97, 4);
}
.stopwatch .buttons .button:active > div {
	--translateY: 1.5em;
}

JavaScript:

Finally, let’s add the functionality to our stopwatch using JavaScript:

const stopWatch = document.querySelector(".stopwatch");
const handles = document.querySelectorAll(".clock .handle .pointer");

const resetButton = document.querySelector(".button-left");
resetButton.addEventListener("click", () => resetTimer());

const startstopButton = document.querySelector(".button-right");
startstopButton.addEventListener("click", () => startstopTimer());

let active = false;

startstopTimer();

function startstopTimer() {
	if (!active) {
		active = true;
		stopWatch.classList.add("running");
	} else {
		active = false;
		stopWatch.classList.remove("running");
	}
}

function resetTimer() {
	active = false;
	stopWatch.classList.remove("running");
	handles.forEach((handle) => {
		handle.style.animation = "none";
		handle.offsetHeight;
		handle.style.animation = null;
	});
}

Click on the above button to check the live demo of stopwatch. You can copy source code easily and implement in your projects.

Must Read: Tic Tac Toe Game Using JavaScript

In this guide, we have developed a fully functional stopwatch with HTML, CSS, and JavaScript. Feel free to adjust the design and features according to your requirements or incorporate it into more extensive web endeavors. As you continue to explore web development, you’ll find countless opportunities to expand on these skills and create even more dynamic and interactive web experiences. Enjoy coding!

Categorized in: