Skip to main content

Modern Neon Clock – Free Analog + Digital JavaScript Clock with Neon Glow Effects

Modern Neon Clock is a free, copy-paste ready analog clock JavaScript component built with pure HTML, CSS, and vanilla JavaScript — zero external libraries. The clock face is a 450px circle using radial-gradient(circle at 30% 30%, #2a2a2a, #0d0d0d) with an 8px solid #1a1a1a border, inner rim via ::after pseudo-element, and a 4-second pulsating @keyframes glow animation cycling outer box-shadow from 60px to 80px spread.

Hand movement is sub-frame smooth using millisecond interpolation: secondDeg = (seconds + milliseconds/1000) * 6, minuteDeg = (minutes + seconds/60) * 6, hourDeg = (hours + minutes/60) * 30. setInterval(updateClock, 50) fires 20 times per second; the CSS transition cubic-bezier(0.4, 0.0, 0.2, 1) smooths between positions for fluid motion. The hour hand is 10px×120px white gradient with glow; the minute hand is 8px×160px; the red second hand is 3px×180px with box-shadow: 0 0 15px rgba(255,68,68,0.6).

What's Included in the Download

How Millisecond-Smooth Hand Movement Works

A standard clock updating once per second produces a ticking second hand — visible jumps. The solution: include now.getMilliseconds() in the rotation calculation. secondDeg = (seconds + milliseconds/1000) * 6 means at exactly 30.5 seconds, the hand is at 31.5 * 6 = 183° rather than snapping between 180° and 186°. At 50ms update frequency, each step is only 0.3° — invisible to the eye. The same fractional interpolation applies to the minute hand (adds seconds/60 to minutes) and the hour hand (adds minutes/60 to hours), so all three hands move continuously rather than stepping.

How Clock Number Positioning Works

Each hour number is placed using polar-to-Cartesian conversion with a radius of 185px. For number at index i: angle = (i * 30 - 90) * Math.PI / 180. The -90 offset rotates the coordinate system so index 0 (12 o'clock) starts at the top rather than the right. Then: x = 185 * Math.cos(angle) + 205, y = 185 * Math.sin(angle) + 205. The +205 offset centers each number in the 450px container (225px center, minus 20px for the 40px number element half-width). The same math pattern — but using transform-origin: 50% 205px and CSS rotation — places all 60 tick marks without any positional math at all.

Best Use Cases

Modern Neon Clock is ideal for: desktop wallpaper web apps, smart home dashboards, new tab page extensions, admin panel time widgets, gaming/stream overlays, kiosk displays, JavaScript learning tutorials (polar math, setInterval, CSS transforms), dark UI portfolios, and any project needing a premium clock widget without a charting library.

License

Released under Creative Commons Attribution 4.0 International (CC BY 4.0). Free for personal and commercial use with attribution to Coodeverse.

Source Code

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Modern Neon Clock</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      background: linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 50%, #0a0a0a 100%);
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    }
    .clock-container {
      position: relative;
      width: 450px;
      height: 450px;
      background: radial-gradient(circle at 30% 30%, #2a2a2a, #0d0d0d);
      border-radius: 50%;
      box-shadow:
        0 0 60px rgba(255, 255, 255, 0.1),
        inset 0 0 40px rgba(0, 0, 0, 0.8),
        0 20px 50px rgba(0, 0, 0, 0.9);
      border: 8px solid #1a1a1a;
    }
    .clock-container::before {
      content: '';
      position: absolute;
      width: 100%;
      height: 100%;
      border-radius: 50%;
      background: radial-gradient(circle at 50% 50%, transparent 60%, rgba(255, 255, 255, 0.02) 100%);
    }
    .clock-container::after {
      content: '';
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      width: 92%;
      height: 92%;
      border-radius: 50%;
      border: 2px solid rgba(255, 255, 255, 0.05);
    }
    .number {
      position: absolute;
      font-size: 32px;
      font-weight: 300;
      color: #fff;
      text-align: center;
      width: 40px;
      height: 40px;
      line-height: 40px;
      text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
      z-index: 2;
      transition: all 0.3s ease;
    }
    .tick {
      position: absolute;
      width: 2px;
      height: 12px;
      background: rgba(255, 255, 255, 0.3);
      left: 50%;
      top: 20px;
      margin-left: -1px;
      transform-origin: 50% 205px;
    }
    .tick.major {
      height: 18px;
      width: 3px;
      background: rgba(255, 255, 255, 0.5);
      margin-left: -1.5px;
    }
    .clock-hand {
      position: absolute;
      transform-origin: bottom center;
      left: 50%;
      bottom: 50%;
      transition: transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
      filter: drop-shadow(0 0 8px rgba(0, 0, 0, 0.8));
    }
    .hour-hand {
      width: 10px;
      height: 120px;
      margin-left: -5px;
      background: linear-gradient(to top, #fff, #e0e0e0);
      border-radius: 8px 8px 0 0;
      z-index: 5;
      box-shadow: 0 0 10px rgba(255, 255, 255, 0.3);
    }
    .minute-hand {
      width: 8px;
      height: 160px;
      margin-left: -4px;
      background: linear-gradient(to top, #fff, #f5f5f5);
      border-radius: 6px 6px 0 0;
      z-index: 6;
      box-shadow: 0 0 10px rgba(255, 255, 255, 0.4);
    }
    .second-hand {
      width: 3px;
      height: 180px;
      margin-left: -1.5px;
      background: linear-gradient(to top, #ff4444, #ff0000);
      border-radius: 3px 3px 0 0;
      z-index: 7;
      box-shadow: 0 0 15px rgba(255, 68, 68, 0.6);
    }
    .center-circle {
      position: absolute;
      width: 24px;
      height: 24px;
      background: radial-gradient(circle, #fff, #ccc);
      border-radius: 50%;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      z-index: 8;
      box-shadow:
        0 0 20px rgba(255, 255, 255, 0.5),
        inset 0 2px 5px rgba(0, 0, 0, 0.3);
      border: 3px solid #1a1a1a;
    }
    .center-circle::after {
      content: '';
      position: absolute;
      width: 8px;
      height: 8px;
      background: #ff4444;
      border-radius: 50%;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      box-shadow: 0 0 10px rgba(255, 68, 68, 0.8);
    }
    .digital-time {
      position: absolute;
      bottom: 80px;
      left: 50%;
      transform: translateX(-50%);
      font-size: 20px;
      font-weight: 300;
      color: #4CAF50;
      text-shadow: 0 0 15px rgba(76, 175, 80, 0.8);
      font-family: 'Courier New', monospace;
      letter-spacing: 2px;
      z-index: 2;
    }
    .date-display {
      position: absolute;
      top: 100px;
      left: 50%;
      transform: translateX(-50%);
      font-size: 14px;
      font-weight: 300;
      color: #aaa;
      text-transform: uppercase;
      letter-spacing: 1px;
      z-index: 2;
    }
    @keyframes glow {
      0%, 100% { box-shadow: 0 0 60px rgba(255, 255, 255, 0.1), inset 0 0 40px rgba(0, 0, 0, 0.8), 0 20px 50px rgba(0, 0, 0, 0.9); }
      50% { box-shadow: 0 0 80px rgba(255, 255, 255, 0.15), inset 0 0 40px rgba(0, 0, 0, 0.8), 0 20px 50px rgba(0, 0, 0, 0.9); }
    }
    .clock-container { animation: glow 4s ease-in-out infinite; }
  </style>
</head>
<body>
  <div class="clock-container">
    <div class="date-display" id="dateDisplay"></div>
    <div class="digital-time" id="digitalTime"></div>
   
    <div class="clock-hand hour-hand" id="hourHand"></div>
    <div class="clock-hand minute-hand" id="minuteHand"></div>
    <div class="clock-hand second-hand" id="secondHand"></div>
    <div class="center-circle"></div>
  </div>

  <script>
    const clockContainer = document.querySelector('.clock-container');
    const radius = 185;
    const numbers = [12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
    // Create hour numbers
    numbers.forEach((num, index) => {
        const angle = (index * 30 - 90) * (Math.PI / 180);
        const x = radius * Math.cos(angle) + 205;
        const y = radius * Math.sin(angle) + 205;
       
        const numberDiv = document.createElement('div');
        numberDiv.className = 'number';
        numberDiv.textContent = num;
        numberDiv.style.left = x + 'px';
        numberDiv.style.top = y + 'px';
        clockContainer.appendChild(numberDiv);
    });
    // Create tick marks
    for (let i = 0; i < 60; i++) {
        const tick = document.createElement('div');
        tick.className = i % 5 === 0 ? 'tick major' : 'tick';
        tick.style.transform = `rotate(${i * 6}deg)`;
        clockContainer.appendChild(tick);
    }
    // Update clock
    function updateClock() {
        const now = new Date();
        const hours = now.getHours() % 12;
        const minutes = now.getMinutes();
        const seconds = now.getSeconds();
        const milliseconds = now.getMilliseconds();
        // Smooth second hand movement
        const secondDeg = (seconds + milliseconds / 1000) * 6;
        const minuteDeg = (minutes + seconds / 60) * 6;
        const hourDeg = (hours + minutes / 60) * 30;
        document.getElementById('hourHand').style.transform = `rotate(${hourDeg}deg)`;
        document.getElementById('minuteHand').style.transform = `rotate(${minuteDeg}deg)`;
        document.getElementById('secondHand').style.transform = `rotate(${secondDeg}deg)`;
        // Update digital time
        const timeString = now.toLocaleTimeString('en-US', {
            hour: '2-digit',
            minute: '2-digit',
            second: '2-digit',
            hour12: false
        });
        document.getElementById('digitalTime').textContent = timeString;
        // Update date
        const dateString = now.toLocaleDateString('en-US', {
            weekday: 'short',
            month: 'short',
            day: 'numeric',
            year: 'numeric'
        });
        document.getElementById('dateDisplay').textContent = dateString;
    }
    updateClock();
    setInterval(updateClock, 50);
  </script>
</body>
</html>

Frequently Asked Questions

Common questions about the free Modern Neon Clock JavaScript analog clock component.

Yes — 100% free under Creative Commons Attribution 4.0. No sign-up, no payment. Click Download Project and use it in personal, commercial, dashboard, or wallpaper projects with attribution to Coodeverse.

Use millisecond interpolation: const ms = now.getMilliseconds(). Then: secondDeg = (seconds + ms/1000) * 6; minuteDeg = (minutes + seconds/60) * 6; hourDeg = (hours + minutes/60) * 30. Run setInterval(updateClock, 50) (20fps). Add CSS transition: transform 0.3s cubic-bezier(0.4,0,0.2,1) on the hand elements for smooth easing. Each update step is only 0.3° — imperceptibly small.

Use polar math: angle = (index * 30 - 90) * Math.PI / 180. The -90 rotates the origin so 12 is at top. Then x = radius * Math.cos(angle) + 205 and y = radius * Math.sin(angle) + 205. The +205 is center offset (450/2 minus half number element width). Set: div.style.left = x + 'px', div.style.top = y + 'px'. Radius is 185 for this 450px clock.

Use box-shadow for outer glow: 0 0 60px rgba(255,255,255,0.1) on the container. Second hand glow: box-shadow: 0 0 15px rgba(255,68,68,0.6). Digital text glow: text-shadow: 0 0 15px rgba(76,175,80,0.8). The pulsating effect: @keyframes glow { 50% { box-shadow: 0 0 80px rgba(255,255,255,0.15)... } } with animation: glow 4s ease-in-out infinite on the container.

Create 60 div elements positioned at the top of the clock face. The key: set transform-origin: 50% 205px on each tick (205px = half of 450px clock height). Then rotate each: tick.style.transform = 'rotate(' + (i * 6) + 'deg)'. Every 5th tick (i % 5 === 0) is major (3px × 18px). Minor ticks are 2px × 12px. CSS transforms do the radial positioning — no position math needed.

Change second hand color (#ff4444) and glow (rgba(255,68,68,0.6)). Change digital display from #4CAF50 to cyan (#00ffff), purple, or blue. To resize, change the 450px width/height and update radius (185) and center offset (205) and tick transform-origin proportionally. Or use transform: scale(0.5) on the outer container for a quick resize without touching JS values.

Browse the full collection at coodeverse.com/projects — Pro Chronometer (stopwatch + CSV), Animated 3D Pie Chart, NEXUS 3D Pure CSS, Neon Toggle Switch, Holographic Login and more. All 100% free with complete HTML CSS JavaScript source code.