Beginner45 minHTMLCSSJavaScript

Build a Digital Clock with JavaScript

A live clock with date, a 12/24-hour toggle and a greeting that changes with the time of day.

What you'll build

Live demo: the finished digital clock & greeting. Try it.
<div class="clock">
  <p id="greeting">Hello</p>
  <h1 id="time">00:00:00</h1>
  <p id="date"></p>
  <button id="format">Switch to 12-hour</button>
</div>
ResultEdit the code, then press Run or Ctrl+Enter

You'll build a big, beautiful clock that ticks every second, shows today's date, says "Good morning/afternoon/evening", and lets the user switch between 12- and 24-hour time.

Along the way you'll use JavaScript's Date object, setInterval, string padding and simple conditions.

How a JavaScript clock works

A web page doesn't have a built-in clock that updates on its own. Instead, you ask JavaScript to run a small function once every second, and each time it runs, the function reads the current time and writes it on the page. That's the whole trick.

Two tools make it possible:

  • The Date object. new Date() gives you the date and time at the exact moment it's created. Methods such as getHours(), getMinutes() and getSeconds() pull out each part as a number.
  • setInterval. setInterval(tick, 1000) calls your tick function every 1,000 milliseconds, which is one second. The timers lesson explains how it differs from setTimeout.

Because tick creates a fresh Date every time, the clock never drifts far from the real time, even if a call arrives a few milliseconds late.

Why the numbers need padding

getMinutes() returns 5, not 05. Shown as is, the clock would jump between 10:5:9 and 10:15:30, changing width every second. String(n).padStart(2, "0") adds a leading zero to any single digit, so every part is always two characters wide. The string methods lesson covers padStart and its partner padEnd.

A monospaced or tabular font helps too. With font-variant-numeric: tabular-nums, each digit takes the same width, so the clock doesn't wobble as the numbers change.

Formatting the date the easy way

You could build the date line by hand: read the day of the week as a number from 0 to 6, look it up in an array of day names, do the same for the month, and join everything together. It works, but it's a lot of code and it only ever speaks one language.

toLocaleDateString does all of that for you. Pass it a locale and an options object describing which parts you want:

JavaScript
const now = new Date();
now.toLocaleDateString("en-GB", { weekday: "long", day: "numeric", month: "long" });
// "Thursday 1 October"

Change "en-GB" to "en-US" and the order switches to "Thursday, October 1". Change it to "fr-FR" and you get the French names. Leave the locale out entirely and the browser uses the visitor's own language settings, which is usually what you want on a real website. The same options work with toLocaleTimeString for the time, including hour12: true for AM and PM.

Writing the clock by hand first and then replacing parts with these built-in methods is a useful exercise. It shows you what the methods are doing behind the scenes, and it's a good reminder to check whether the language already solves a problem before you write your own solution.

Turn it into an analog clock

Once your digital clock works, an analog clock is a short step away, and it uses the same tick() function. Each hand is a thin element that rotates around the centre of the clock face. All you need is the angle for each hand:

  • Seconds: 360 degrees Γ· 60 seconds = 6 degrees per second.
  • Minutes: also 6 degrees per minute, plus a little extra for the seconds so the hand moves smoothly.
  • Hours: 360 Γ· 12 = 30 degrees per hour, plus 0.5 degrees per minute.
JavaScript
const s = now.getSeconds();
const m = now.getMinutes();
const h = now.getHours() % 12;

secondHand.style.transform = `rotate(${s * 6}deg)`;
minuteHand.style.transform = `rotate(${m * 6 + s * 0.1}deg)`;
hourHand.style.transform = `rotate(${h * 30 + m * 0.5}deg)`;

In CSS, give each hand position: absolute, put its bottom at the centre of the face and set transform-origin: bottom center so it spins around that point rather than its middle. The CSS 2D transforms lesson covers rotation and transform origins.

Requirements checklist

  • Time displayed as HH:MM:SS, always two digits per part
  • Updates every second with setInterval
  • Today's date like "Thursday 1 October"
  • A greeting based on the hour: morning (5 to 11), afternoon (12 to 17), evening (18 to 21), night otherwise
  • A button that toggles 12/24-hour format (12-hour shows AM/PM)

Starter code

Press Run to see where you're starting from, then follow the steps.

// Your clock code
ResultEdit the code, then press Run or Ctrl+Enter

Step-by-step guide

  1. 1

    Read the current time

    new Date() gives the current date and time. Its methods return numbers.

    JavaScript
    const now = new Date();
    now.getHours();   // 0 to 23
    now.getMinutes(); // 0 to 59
    now.getSeconds(); // 0 to 59
  2. 2

    Format with padStart

    7 should show as 07. Convert to a string and pad it.

    JavaScript
    const pad = (n) => String(n).padStart(2, "0");
  3. 3

    Write the tick function

    JavaScript
    const timeEl = document.querySelector("#time");
    const dateEl = document.querySelector("#date");
    const greetingEl = document.querySelector("#greeting");
    let use24 = true;
    
    function tick() {
      const now = new Date();
      let h = now.getHours();
      let suffix = "";
    
      if (!use24) {
        suffix = h >= 12 ? " PM" : " AM";
        h = h % 12 || 12;
      }
    
      timeEl.textContent = `${pad(h)}:${pad(now.getMinutes())}:${pad(now.getSeconds())}${suffix}`;
      dateEl.textContent = now.toLocaleDateString("en-GB", { weekday: "long", day: "numeric", month: "long" });
      greetingEl.textContent = greeting(now.getHours());
    }

    h % 12 || 12 turns 0 into 12 and 13 into 1.

  4. 4

    The greeting

    JavaScript
    function greeting(hour) {
      if (hour >= 5 && hour < 12) return "Good morning β˜€οΈ";
      if (hour >= 12 && hour < 18) return "Good afternoon 🌀";
      if (hour >= 18 && hour < 22) return "Good evening πŸŒ†";
      return "Good night πŸŒ™";
    }
  5. 5

    Start ticking and add the toggle

    JavaScript
    tick();
    setInterval(tick, 1000);
    
    document.querySelector("#format").addEventListener("click", (e) => {
      use24 = !use24;
      e.target.textContent = use24 ? "Switch to 12-hour" : "Switch to 24-hour";
      tick();
    });

    Call tick() once straight away so the clock doesn't show 00:00:00 for the first second.

Final solution

Show the complete solution
const timeEl = document.querySelector("#time");
const dateEl = document.querySelector("#date");
const greetingEl = document.querySelector("#greeting");
let use24 = true;

const pad = (n) => String(n).padStart(2, "0");

function greeting(hour) {
  if (hour >= 5 && hour < 12) return "Good morning β˜€οΈ";
  if (hour >= 12 && hour < 18) return "Good afternoon 🌀";
  if (hour >= 18 && hour < 22) return "Good evening πŸŒ†";
  return "Good night πŸŒ™";
}

function tick() {
  const now = new Date();
  let h = now.getHours();
  let suffix = "";

  if (!use24) {
    suffix = h >= 12 ? " PM" : " AM";
    h = h % 12 || 12;
  }

  timeEl.textContent = `${pad(h)}:${pad(now.getMinutes())}:${pad(now.getSeconds())}${suffix}`;
  dateEl.textContent = now.toLocaleDateString("en-GB", { weekday: "long", day: "numeric", month: "long" });
  greetingEl.textContent = greeting(now.getHours());
}

tick();
setInterval(tick, 1000);

document.querySelector("#format").addEventListener("click", (e) => {
  use24 = !use24;
  e.target.textContent = use24 ? "Switch to 12-hour" : "Switch to 24-hour";
  tick();
});
ResultEdit the code, then press Run or Ctrl+Enter

Bonus challenges

  • Add a stopwatch with Start, Stop and Reset buttons.
  • Show clocks for three cities using toLocaleTimeString("en-GB", { timeZone: "Asia/Tokyo" }).
  • Change the background colour gradually through the day with HSL and the current hour.

Common mistakes and how to fix them

The clock appears one second late. setInterval waits a full second before the first call. Call tick() once straight away, then start the interval.

Midnight shows as 0 in 12-hour mode. In 12-hour time, hour 0 should read 12. Use hours % 12 || 12: the remainder gives 0 for both midnight and noon, and || 12 swaps that 0 for 12.

The greeting is wrong at the boundaries. Check your conditions carefully. With hour < 12 for morning and hour < 18 for afternoon, 12:00 is afternoon and 18:00 is evening. Write the ranges down before coding them, as the if...else lesson suggests.

Months appear one too low. getMonth() counts from 0, so January is 0. Using toLocaleDateString with options avoids this problem entirely and gives you the day and month names for free.

Check your work

  1. Watch the seconds roll over from 59 to 00. The minute should go up at the same moment.
  2. Toggle between 12-hour and 24-hour time a few times. AM and PM should only appear in 12-hour mode.
  3. Change your computer's clock (or temporarily replace new Date() with a fixed date such as new Date(2026, 0, 1, 23, 59, 55)) to test midnight and the greeting boundaries.
  4. Leave the tab in the background for a minute and come back. The time should still be correct.

Clock, countdown or stopwatch?

The same building blocks power three different tools. A clock reads the real time on every tick, as you've built here. A countdown timer stores a target date and shows the difference between now and then: target - Date.now() gives the milliseconds left, which you divide into days, hours, minutes and seconds. A stopwatch records Date.now() when you press Start and shows how much time has passed since.

All three should calculate from Date.now() on every tick rather than adding one second each time. Timers can fire late, especially in background tabs, and counting ticks slowly drifts away from the real time, while measuring the difference stays accurate. Building a countdown to a birthday or a product launch is a great next project that reuses almost all of this code.

Questions beginners ask

Why does the clock slow down in a background tab? Browsers save battery by running timers less often in hidden tabs. Because the clock reads the real time on every tick, it catches up as soon as you return.

Can I show the time in another country? Yes. toLocaleTimeString accepts a timeZone option, which is the second bonus challenge. The dates lesson shows more formatting options.

What should I build next? The tip calculator practises reading user input, and the to-do app adds saving data between visits.