A complete, step-by-step coding tutorial to build a live, animated, mobile-friendly digital clock from scratch — no prior JavaScript experience required.
The glowing clock at the top of this page isn't a screenshot — it's live code, updating every second based on your actual system time. In this tutorial, we'll break that exact project down piece by piece, so you don't just copy the code — you understand why every single line is there.
00Project Overview
Our digital clock is made of the same three building blocks as almost every web project:
- HTML — the structure: the box that holds the time and the date.
- CSS — the styling: the dark background, neon glow, glassmorphism card, and pulsing animation.
- JavaScript — the logic: reading the real time every second and displaying it on the page.
01Step 1 — Building the HTML Structure
First, we need two empty elements: one for the time, one for the date. Both live inside a card-like container called .clock-box.
<div class="clock-box">
<div class="clock" id="clock">00:00:00 AM</div>
<div class="date" id="date">Sunday, 1 January 2025</div>
</div>
The most important part here is id="clock" and id="date". These IDs tell JavaScript exactly which box to update. Without them, JavaScript wouldn't know which element the new time should be written into.
class is used for styling with CSS, while an id uniquely identifies one specific element — especially useful when you need to target it with JavaScript.
02Step 2 — CSS Reset & Background
Like most projects, we start by removing the browser's default spacing and setting box-sizing: border-box so padding and borders never mess up an element's width.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: Arial, Helvetica, sans-serif;
}
Next, we center everything on the screen using Flexbox and apply a deep navy gradient background:
body {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #04101f, #0a1f3d, #071428);
color: #fff;
}
min-height: 100vh makes the background fill the entire screen height, on both mobile and desktop. display: flex combined with align-items and justify-content keeps the clock card perfectly centered at all times.
03Step 3 — The Glow Effect (Pseudo-Elements)
Those soft blue and purple lights slowly drifting in the background aren't images — they're two "virtual elements" called body::before and body::after.
body::before {
content: "";
position: absolute;
width: 500px;
height: 500px;
border-radius: 50%;
background: radial-gradient(circle, rgba(0, 245, 255, 0.25), transparent 70%);
top: -120px;
left: -120px;
filter: blur(20px);
animation: moveGlow 8s ease-in-out infinite alternate;
}
This is a large, blurred circle positioned partly off-screen, so only its soft light bleeds into view — never the sharp circle itself. The animation: moveGlow 8s ... infinite alternate rule slowly drifts it back and forth, which is what makes the whole page feel alive.
What do the @keyframes actually do?
@keyframes moveGlow {
0% { transform: translate(0, 0) scale(1); }
100% { transform: translate(40px, 30px) scale(1.12); }
}
This simply says: "start in place, and by the end of 8 seconds, drift slightly down-right while growing a bit bigger." The alternate keyword makes the motion reverse smoothly, creating a continuous, gentle wave-like effect.
04Step 4 — Glassmorphism Card Design
The frosted-glass look of the clock card comes down to just three CSS properties working together:
.clock-box {
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.14);
backdrop-filter: blur(18px);
-webkit-backdrop-filter: blur(18px);
border-radius: 30px;
box-shadow: 0 15px 50px rgba(0, 0, 0, 0.45);
}
rgba(255,255,255,0.08)— a nearly transparent white so the glow behind it stays visible.backdrop-filter: blur(18px)— blurs everything behind the card, creating that "frosted glass" look.box-shadow— lifts the card slightly off the page, making it feel physical and real.
-webkit-backdrop-filter line is included so this effect also works correctly in Safari and older mobile browsers.
05Step 5 — Responsive Typography with clamp()
A common beginner mistake is setting a fixed font size (like 80px), which then overflows on small screens. This project instead uses the clamp() function:
.clock {
font-size: clamp(3rem, 10vw, 6rem);
}
This means: "never go smaller than 3rem, never go larger than 6rem, and in between, scale automatically based on the screen width (10vw)." That's exactly why this clock looks great on both mobile and desktop without needing a separate media query just for font size.
06Step 6 — The Pulse Animation
The time slightly "breathes" as it glows. That effect comes from a simple combination of scale and text-shadow animation:
@keyframes pulse {
0%, 100% {
transform: scale(1);
text-shadow: 0 0 12px rgba(0, 245, 255, 0.85), 0 0 25px rgba(0, 245, 255, 0.45);
}
50% {
transform: scale(1.01);
text-shadow: 0 0 18px rgba(0, 245, 255, 1), 0 0 38px rgba(0, 245, 255, 0.6);
}
}
At the 50% mark, the numbers grow very slightly larger and glow brighter, then settle back to normal — this continuous loop is what creates the illusion of a living, glowing light.
07Step 7 — The Real Logic: Reading Time with JavaScript
This is the most important part of the whole project. First, we grab the current moment using the built-in Date object:
function updateClock() {
const now = new Date();
let hours = now.getHours();
const minutes = String(now.getMinutes()).padStart(2, "0");
const seconds = String(now.getSeconds()).padStart(2, "0");
const ampm = hours >= 12 ? "PM" : "AM";
...
}
There are four key concepts to learn here:
new Date()— pulls the current hour, minute, second, day, and year straight from the computer's clock.getHours(),getMinutes(),getSeconds()— return numbers from 0–23 (hours) and 0–59 (minutes/seconds).padStart(2, "0")— if a number is a single digit (like5), this turns it into05, so the clock always shows two digits.hours >= 12 ? "PM" : "AM"— a "ternary operator," a shorthand way of writing a simple if/else statement.
Converting 24-hour time into 12-hour format
hours = hours % 12;
hours = hours ? hours : 12;
hours = String(hours).padStart(2, "0");
Computers count hours from 0 to 23, but we want to display them like a normal clock, from 1 to 12. hours % 12 (the modulo/remainder operation) turns 13 into 1, and turns 0 into 0. Since midnight would otherwise show as 0 (which is wrong), the next line hours ? hours : 12 converts that 0 into 12.
Displaying the text on the page
document.getElementById("clock").textContent =
`${hours}:${minutes}:${seconds} ${ampm}`;
document.getElementById("clock") finds the exact element with id="clock", and .textContent replaces the text inside it. Here we use "template literals" (backticks with ${ }) to combine variables into a single readable string easily.
Building the date
const days = ["Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"];
const months = ["January", "February", "March", ... , "December"];
const dayName = days[now.getDay()];
const monthName = months[now.getMonth()];
getDay() returns a number for the day of the week (0 = Sunday), and getMonth() returns a number for the month (0 = January). Since these are just numbers and not human-readable names, we first build two arrays (lists), then use those exact numbers to look up the correct name.
Updating automatically, every second
updateClock();
setInterval(updateClock, 1000);
We run the function once immediately (so the correct time shows the moment the page loads), then setInterval re-runs it every 1000 milliseconds (one second) after that — that one trick is what makes the clock feel truly "alive."
08Step 8 — Making It Mobile-Friendly
Finally, a small media query slightly reduces padding and border radius on smaller screens, so the card stays well-proportioned on mobile:
@media (max-width: 600px) {
.clock-box {
padding: 30px 18px;
border-radius: 24px;
}
}
Since the font size is already responsive thanks to clamp(), only minor spacing adjustments were needed — good practice is to rely on clamp()/%/vw as much as possible so you need fewer media queries overall.
🎯 Try It Yourself (Practice Tasks)
Once you've read through the whole tutorial, try these on your own:
- Change the glow color to green or pink — just swap out
rgba(0,245,255, ...)for a new color. - Add a button that toggles between 12-hour and 24-hour time format.
- Give the seconds digit a different color than the rest of the time.
- Try adding a simple "alarm" feature (hint: use an
ifstatement to compare against the current time).
Summary
In this one project, we learned how to:
- Give HTML elements unique
ids so JavaScript can control them. - Build a beautiful background using Flexbox, gradients, and pseudo-elements.
- Create a glassmorphism effect with
backdrop-filter. - Build fully responsive design using
clamp()and media queries. - Use the
Dateobject andsetIntervalto build a real-time feature.
These are the exact same fundamentals used later to build countdown timers, stopwatches, and even full web apps. Press the "Open Full Project" button above to view this clock as a separate, standalone page and copy the full source code into your own editor.
Frequently Asked Questions
Do I need to know JavaScript before starting this project?
No. This tutorial explains every JavaScript concept used — including the Date object, padStart(), ternary operators, and setInterval() — from scratch, in plain language.
Why does the clock use clamp() instead of a fixed font size?
clamp() lets the font size scale smoothly between a minimum and maximum value based on screen width, so the clock looks correct on both mobile phones and large desktop monitors without extra code.
Will this clock work on all browsers?
Yes. It uses standard CSS and JavaScript, and includes the -webkit-backdrop-filter fallback so the glass effect also renders correctly in Safari.
Can I customize the colors and animation speed?
Absolutely. All colors are defined using rgba() values in the CSS, and animation timing is controlled by the seconds value in each animation property, so both are easy to edit.

0 Comments
Have a question or suggestion? Share your thoughts below — we’d love to hear from you!