Home Module 15 Digital Clock

Project Overview

Build a real-time digital clock that updates every second. This project practises: setInterval, the Date object, string formatting with padStart, DOM updates, and event listeners.

What you will build

  • Large digital display showing HH:MM:SS
  • AM/PM indicator
  • Current date (e.g. Sunday, 1 June 2026)
  • Toggle between 12-hour and 24-hour format
  • Dark / light mode toggle

Concepts used

  • setInterval for recurring execution
  • new Date() and its methods
  • String.prototype.padStart for zero-padding
  • DOM content update (textContent)
  • localStorage for preference persistence

Step-by-Step Build

Step 1 — HTML structure

<div class="clock-wrapper">
  <div id="time">00:00:00</div>
  <div id="period">AM</div>
  <div id="date"></div>
  <div class="controls">
    <button id="format-btn">24h</button>
    <button id="theme-btn">Dark Mode</button>
  </div>
</div>

Step 2 — Get the current time

function getTimeParts() {
  const now = new Date();
  return {
    hours:   now.getHours(),
    minutes: now.getMinutes(),
    seconds: now.getSeconds(),
    day:     now.toLocaleDateString('en-GB', {
               weekday: 'long', day: 'numeric',
               month: 'long', year: 'numeric'
             })
  };
}

Step 3 — Format and display

function updateClock() {
  const { hours, minutes, seconds, day } = getTimeParts();
  const is24h = localStorage.getItem('clock_24h') === 'true';

  let displayHours = hours;
  let period = '';

  if (!is24h) {
    period = hours >= 12 ? 'PM' : 'AM';
    displayHours = hours % 12 || 12; // 0 → 12
  }

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

  document.querySelector('#time').textContent =
    `${pad(displayHours)}:${pad(minutes)}:${pad(seconds)}`;
  document.querySelector('#period').textContent = period;
  document.querySelector('#date').textContent   = day;
}

Step 4 — Start the interval

updateClock(); // show immediately
setInterval(updateClock, 1000);

Step 5 — Controls

document.querySelector('#format-btn').addEventListener('click', () => {
  const current = localStorage.getItem('clock_24h') === 'true';
  localStorage.setItem('clock_24h', !current);
  document.querySelector('#format-btn').textContent = !current ? '12h' : '24h';
  updateClock();
});

document.querySelector('#theme-btn').addEventListener('click', () => {
  document.body.classList.toggle('dark');
  const isDark = document.body.classList.contains('dark');
  localStorage.setItem('clock_theme', isDark ? 'dark' : 'light');
  document.querySelector('#theme-btn').textContent = isDark ? 'Light Mode' : 'Dark Mode';
});

// Apply saved preferences on load
if (localStorage.getItem('clock_theme') === 'dark') document.body.classList.add('dark');
if (localStorage.getItem('clock_24h') === 'true') {
  document.querySelector('#format-btn').textContent = '12h';
}

Complete Working Code

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Digital Clock</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
      min-height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
      background: #1a1a2e;
      color: #e0e0e0;
      font-family: 'Courier New', monospace;
      transition: background .3s, color .3s;
    }
    body.light { background: #f0f4f8; color: #1a1a2e; }
    .clock-wrapper { text-align: center; }
    #time {
      font-size: clamp(3rem, 12vw, 7rem);
      font-weight: 700;
      letter-spacing: 0.05em;
      color: #00d4ff;
      text-shadow: 0 0 20px #00d4ff88;
    }
    body.light #time { color: #0066cc; text-shadow: none; }
    #period {
      font-size: 1.5rem;
      letter-spacing: .2em;
      margin: .5rem 0;
      opacity: .7;
    }
    #date { font-size: 1.1rem; opacity: .6; margin-bottom: 2rem; }
    .controls { display: flex; gap: 1rem; justify-content: center; }
    button {
      padding: .5rem 1.25rem;
      border: 2px solid currentColor;
      background: transparent;
      color: inherit;
      border-radius: 999px;
      cursor: pointer;
      font-size: .9rem;
      transition: background .2s, color .2s;
    }
    button:hover { background: currentColor; color: #1a1a2e; }
  </style>
</head>
<body>
  <div class="clock-wrapper">
    <div id="time">00:00:00</div>
    <div id="period"></div>
    <div id="date"></div>
    <div class="controls">
      <button id="format-btn">24h</button>
      <button id="theme-btn">Light Mode</button>
    </div>
  </div>

  <script>
    const timeEl   = document.querySelector('#time');
    const periodEl = document.querySelector('#period');
    const dateEl   = document.querySelector('#date');
    const fmtBtn   = document.querySelector('#format-btn');
    const thmBtn   = document.querySelector('#theme-btn');

    function is24h() { return localStorage.getItem('clock_24h') === 'true'; }

    function updateClock() {
      const now = new Date();
      let hours   = now.getHours();
      const mins  = now.getMinutes();
      const secs  = now.getSeconds();
      const pad   = n => String(n).padStart(2, '0');

      let period = '';
      if (!is24h()) {
        period = hours >= 12 ? 'PM' : 'AM';
        hours  = hours % 12 || 12;
      }

      timeEl.textContent   = `${pad(hours)}:${pad(mins)}:${pad(secs)}`;
      periodEl.textContent = period;
      dateEl.textContent   = now.toLocaleDateString('en-GB', {
        weekday: 'long', day: 'numeric', month: 'long', year: 'numeric'
      });
    }

    fmtBtn.addEventListener('click', () => {
      localStorage.setItem('clock_24h', !is24h());
      fmtBtn.textContent = is24h() ? '12h' : '24h';
      updateClock();
    });

    thmBtn.addEventListener('click', () => {
      document.body.classList.toggle('light');
      const isLight = document.body.classList.contains('light');
      localStorage.setItem('clock_theme', isLight ? 'light' : 'dark');
      thmBtn.textContent = isLight ? 'Dark Mode' : 'Light Mode';
    });

    if (localStorage.getItem('clock_theme') === 'light') {
      document.body.classList.add('light');
      thmBtn.textContent = 'Dark Mode';
    }
    if (is24h()) fmtBtn.textContent = '12h';

    updateClock();
    setInterval(updateClock, 1000);
  </script>
</body>
</html>

Code Explained

padStart(2, '0')

String(5).padStart(2, '0') produces '05' — it pads the start of the string with zeros until the total length is 2. This is why we get 09:05:07 instead of 9:5:7.

hours % 12 || 12

12-hour conversion: hours % 12 gives 0 for both midnight (0) and noon (12). The || 12 replaces 0 with 12 — making midnight show as 12:xx AM.

setInterval vs setTimeout

setInterval(fn, 1000) calls fn every 1000ms indefinitely. We call updateClock() once immediately before starting the interval so the display shows instantly on load (not after a 1-second blank).

Challenges

  1. Add a milliseconds display that updates every 100ms.
  2. Add a blinking colon separator (hide/show every half-second using setInterval).
  3. Add a timezone selector (<select>) using Intl.DateTimeFormat with a timeZone option.
  4. Add an alarm: user picks a time, an alert fires when the clock hits it.
  5. Display elapsed time since midnight as a progress bar.

Improvement Ideas

  • Use a monospace font or a 7-segment style web font for an authentic digital look.
  • Add a smooth CSS transition when the numbers change.
  • Show a small world clock with 3 cities in different timezones.
  • Replace the text display with an SVG or canvas-drawn clock face for an analogue version.