Loading...

Web Development Projects

Complete video lectures, notes, and source code.

Modern Digital Clock

Source Code

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Modern Digital Clock & Alarms</title>
    <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Roboto:wght@300;400;500&family=VT323&display=swap" rel="stylesheet">
    <style>
        :root {
            --primary-color: #00f2ff;
            --bg-color: #121212;
            --card-bg: #1e1e1e;
            --text-color: #ffffff;
            --danger-color: #ff4757;
        }

        body {
            font-family: 'Roboto', sans-serif;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            min-height: 100vh;
            background-color: var(--bg-color);
            color: var(--text-color);
            margin: 0;
        }

        .container {
            background-color: var(--card-bg);
            padding: 40px;
            border-radius: 20px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.5);
            text-align: center;
            width: 350px;
            position: relative;
            z-index: 1;
        }

        #clock {
            font-family: 'VT323', monospace;
            font-size: 80px;
            color: var(--primary-color);
            margin-bottom: 30px;
            text-shadow: 0 0 10px rgba(0, 242, 255, 0.5);
        }

        .input-group {
            display: flex;
            justify-content: center;
            gap: 10px;
            margin-bottom: 20px;
        }

        input {
            width: 50px;
            padding: 10px;
            background: #2c2c2c;
            border: 1px solid #333;
            border-radius: 8px;
            color: white;
            font-size: 18px;
            text-align: center;
            font-family: 'VT323', monospace;
            font-size: 24px;
        }

        input:focus {
            outline: none;
            border-color: var(--primary-color);
        }

        button {
            padding: 12px 24px;
            font-size: 16px;
            cursor: pointer;
            border: none;
            border-radius: 8px;
            font-weight: 500;
            transition: transform 0.1s, opacity 0.2s;
        }

        button:active {
            transform: scale(0.98);
        }

        #addAlarmBtn {
            background-color: var(--primary-color);
            color: #000;
            width: 100%;
            margin-bottom: 20px;
        }

        #stopAlarmBtn {
            background-color: var(--danger-color);
            color: white;
            width: 100%;
            display: none;
            animation: pulse 1s infinite;
        }

        .alarm-list {
            list-style: none;
            padding: 0;
            margin: 0;
            max-height: 200px;
            overflow-y: auto;
            text-align: left;
        }

        .alarm-item {
            background: #2c2c2c;
            padding: 10px 15px;
            border-radius: 8px;
            margin-bottom: 8px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            font-family: 'VT323', monospace;
            font-size: 22px;
        }

        .delete-btn {
            background: transparent;
            color: var(--danger-color);
            padding: 5px;
            font-size: 18px;
        }

        .delete-btn:hover {
            color: #ff6b81;
        }

        @keyframes pulse {
            0% { box-shadow: 0 0 0 0 rgba(255, 71, 87, 0.4); }
            70% { box-shadow: 0 0 0 10px rgba(255, 71, 87, 0); }
            100% { box-shadow: 0 0 0 0 rgba(255, 71, 87, 0); }
        }
        
        /* Scrollbar styling */
        .alarm-list::-webkit-scrollbar {
            width: 5px;
        }
        .alarm-list::-webkit-scrollbar-thumb {
            background: #444; 
            border-radius: 5px;
        }
    </style>
</head>
<body>

    <div class="container">
        <div id="clock">00:00:00</div>

        <div class="input-group">
            <input type="number" id="hour" placeholder="HH" min="0" max="23" value="00">
            <input type="number" id="minute" placeholder="MM" min="0" max="59" value="00">
            <input type="number" id="second" placeholder="SS" min="0" max="59" value="00">
        </div>

        <button id="addAlarmBtn" onclick="addAlarm()">Add Alarm</button>
        <button id="stopAlarmBtn" onclick="stopAlarm()">STOP ALARM</button>

        <ul class="alarm-list" id="alarmList">
            <!-- Alarms will appear here -->
        </ul>
    </div>

    <!-- Ensure you have an 'alarm.mp3' file in the same directory -->
    <audio id="alarmSound" src="mixkit-classic-alarm-995.wav" loop></audio>

    <script>
        let alarms = [];
        let isRinging = false;
        const audio = document.getElementById('alarmSound');
        const alarmListEl = document.getElementById('alarmList');

        function updateTime() {
            const now = new Date();
            const h = String(now.getHours()).padStart(2, '0');
            const m = String(now.getMinutes()).padStart(2, '0');
            const s = String(now.getSeconds()).padStart(2, '0');
            const currentTime = `${h}:${m}:${s}`;

            document.getElementById('clock').innerText = currentTime;

            // Check alarms
            const alarmsTriggered = alarms.filter(a => a.time === currentTime);
            if (alarmsTriggered.length > 0 && !isRinging) {
                startAlarm();
                // Remove triggered alarms from the list
                alarms = alarms.filter(a => a.time !== currentTime);
                renderAlarms();
            }
        }

        function startAlarm() {
            isRinging = true;
            audio.play().catch(e => console.error("Audio play failed:", e));
            document.getElementById('addAlarmBtn').style.display = 'none';
            document.getElementById('stopAlarmBtn').style.display = 'block';
        }

        function stopAlarm() {
            isRinging = false;
            audio.pause();
            audio.currentTime = 0;
            document.getElementById('addAlarmBtn').style.display = 'block';
            document.getElementById('stopAlarmBtn').style.display = 'none';
        }

        function addAlarm() {
            const h = String(document.getElementById('hour').value).padStart(2, '0');
            const m = String(document.getElementById('minute').value).padStart(2, '0');
            const s = String(document.getElementById('second').value).padStart(2, '0');
            
            const timeString = `${h}:${m}:${s}`;
            
            // Prevent duplicates
            if (alarms.some(a => a.time === timeString)) {
                alert("Alarm already set for this time!");
                return;
            }

            alarms.push({ time: timeString });
            alarms.sort((a, b) => a.time.localeCompare(b.time)); // Sort by time
            renderAlarms();
            
            // Unlock audio on user interaction (for browsers that block autoplay)
            if (audio.paused) {
                audio.play().then(() => {
                    audio.pause();
                    audio.currentTime = 0;
                }).catch(e => {});
            }
        }

        function removeAlarm(index) {
            alarms.splice(index, 1);
            renderAlarms();
        }

        function renderAlarms() {
            alarmListEl.innerHTML = '';
            alarms.forEach((alarm, index) => {
                const li = document.createElement('li');
                li.className = 'alarm-item';
                li.innerHTML = `
                    <span>${alarm.time}</span>
                    <button class="delete-btn" onclick="removeAlarm(${index})">×</button>
                `;
                alarmListEl.appendChild(li);
            });
        }

        setInterval(updateTime, 1000);
        updateTime(); // Initial call to avoid 1s delay
    </script>
    <script src="script.js"></script>
</body>
</html>
CSS
body { background-color: #f4f4f4; } h1 { color: #333; }
JavaScript
document.addEventListener('DOMContentLoaded', function() {
  var div = document.createElement('div'),
      canvas = document.createElement('canvas'),    
      ctx = canvas.getContext('2d'),
      w,
      h,
      msTimer = 0.0,
      lightningTimer,
      lightningAlpha,
      rainArr = [50],
      rainSpeed = 4;

  // initialize
  function init() {
    document.body.appendChild(div);
    div.style.position = "fixed";
    div.appendChild(canvas);
    UpdatePosition();
    create_rain();
    lightningTimer = 8000.0;
    lightningAlpha = 0.0;

    // 1 frame every 30ms
    if (typeof game_loop != "undefined") clearInterval(game_loop);
    game_loop = setInterval(mainLoop, 30);
  }
  init();

  function create_rain() {
    var length = 500;
    rainArr = []; //Empty array to start with
    for (var i = length - 1; i >= 0; i--) {
      rainArr.push({
        x: 1,
        y: 0,
        z: 0
      });
    }

    for (var j = 0; j < 500; j++) {
      rainArr[j].x = Math.floor((Math.random() * 820) - 9);
      rainArr[j].y = Math.floor((Math.random() * 520) - 9);
      rainArr[j].z = Math.floor((Math.random() * 2) + 1);
      rainArr[j].w = Math.floor((Math.random() * 3) + 2);
    }
  }

  function mainLoop() {
    UpdatePosition();
    msTimer += 30;

    if (lightningTimer < 0.0)  {
      lightningTimer = 8000.0;
    }
    else {
      lightningTimer -= 30.0;
    }    

    ctx.fillStyle = "#202426";
    ctx.fillRect(0,0,w,h);

    sidewalk();
    road();
    lamp();        
    rain();

    if (lightningTimer < 500.0) {
      weather(lightningTimer);
    }

    ctx.fillStyle = 'rgba(255, 255, 255, .1)';
    ctx.font = '30px Sans-Serif';
    ctx.fillText("Ashish", w - 150, 492);    
  }
  
  
  // canvas positioning and sizing
  function UpdatePosition () {
    var bodyWidth = document.documentElement.clientWidth,
        bodyHeight = document.documentElement.clientHeight;
    w = canvas.width = Math.max(500,bodyWidth);
    h = canvas.height = Math.max(320,bodyHeight);
    div.style.left=div.style.right=
      div.style.top=div.style.bottom="0";
  }

  // lamp visuals
  function lamp() {
    var grd = ctx.createLinearGradient(150, 210, 150, 500);
    grd.addColorStop(0.000, 'rgba(60, 60, 60, 1.000)');
    grd.addColorStop(0.2, 'rgba(80, 80, 80, 1.000)');
    grd.addColorStop(1, 'rgba(45, 45, 45, 1.000)');        
    ctx.fillStyle = grd;
    ctx.fillRect(247, 210, 6, 290);

    var grdOuterHigh = ctx.createLinearGradient(150, 210, 150, 500);        
    grdOuterHigh.addColorStop(0.000, 'rgba(65, 65, 65, 1.000)');
    grdOuterHigh.addColorStop(0.2, 'rgba(95, 95, 95, 1.000)');
    grdOuterHigh.addColorStop(1, 'rgba(47, 47, 47, 1.000)');
    ctx.fillStyle = grdOuterHigh;
    ctx.fillRect(246, 210, 1, 290);

    var grdOuterLow = ctx.createLinearGradient(150, 210, 150, 500);        
    grdOuterLow.addColorStop(0.000, 'rgba(45, 45, 45, 1.000)');
    grdOuterLow.addColorStop(0.2, 'rgba(60, 60, 60, 1.000)');
    grdOuterLow.addColorStop(1, 'rgba(43, 43, 43, 1.000)');
    ctx.fillStyle = grdOuterLow;
    ctx.fillRect(253, 210, 1, 290);

    // glow modified by time passed
    var sinGlowMod = 5 * Math.sin(msTimer / 200);
    var cosGlowMod = 5 * Math.cos((msTimer + 0.5 * sinGlowMod) / 200);        
    var grdGlow = ctx.createRadialGradient(250, 200, 0, 247 + sinGlowMod,
                                           400, 206 + cosGlowMod);
    grdGlow.addColorStop(0.000, 'rgba(220, 240, 160, 1)');
    grdGlow.addColorStop(0.2, 'rgba(180, 240, 160, 0.4)');
    grdGlow.addColorStop(0.4, 'rgba(140, 240, 160, 0.2)');
    grdGlow.addColorStop(1, 'rgba(140, 240, 160, 0)');
    ctx.fillStyle = grdGlow;
    ctx.fillRect(0, 0, 500, 500);
  }

  // function to position and color each rain drop
  // TODO: optimize - group raindrops together
  function rain() {   
    for (var i = 0; i < 500; i++) {
      if  (rainArr[i].y >= 482) {
        rainArr[i].y-=500;
      }
      if  (rainArr[i].x < -10) {
        rainArr[i].x+=w;
      }
      else {
        rainArr[i].y += rainArr[i].w * rainSpeed;
        rainArr[i].x -= 5 + Math.floor(rainArr[i].y / 250) - rainArr[i].w;
      }

      var grd = ctx.createRadialGradient(250, 450, 140, 250, 300, 600);
      grd.addColorStop(0.000, 'rgba(100, 170, 160, 0.2)');
      grd.addColorStop(0.1, 'rgba(100, 160, 160, 0.12)');
      grd.addColorStop(0.2, 'rgba(100, 150, 150, 0.1)');
      grd.addColorStop(1, 'rgba(100, 140, 140, .08)');
      ctx.fillStyle = grd;
      ctx.fillRect(rainArr[i].x, rainArr[i].y, rainArr[i].z, 4);
    }
  }
  
  // sidewalk visuals
  function sidewalk()
  {
    ctx.fillStyle = '#343A34';
    ctx.fillRect(0,500,w,10);
    var grd = ctx.createRadialGradient(250, 500, 0,
                                       250, 500, 150);
    grd.addColorStop(0.0, 'rgba(32, 36, 38, .0)');
    grd.addColorStop(0.2, 'rgba(32, 36, 38, 0.1)');
    grd.addColorStop(0.6, 'rgba(32, 36, 38, 0.2)');
    grd.addColorStop(0.8, 'rgba(32, 36, 38, 0.6)');
    grd.addColorStop(1, 'rgba(32, 34, 38, .8)');
    ctx.fillStyle = grd;
    ctx.fillRect(0,500,w,10);
    
    ctx.fillStyle = '#343A34';
    ctx.fillRect(0,510,w,10);
    grd = ctx.createRadialGradient(250, 500, 0,
                                       250, 500, 150);
    grd.addColorStop(0.0, 'rgba(32, 36, 38, 0.7)');
    grd.addColorStop(0.2, 'rgba(32, 36, 38, 0.8)');
    grd.addColorStop(0.4, 'rgba(32, 36, 38, 0.85)');
    grd.addColorStop(1, 'rgba(32, 34, 38, .9)');
    ctx.fillStyle = grd;
    ctx.fillRect(0,510,w,10);
  }
  
  
  // road visuals
  function road() {
    ctx.fillStyle = '#202224';
    ctx.fillRect(0,520,w,h-520);
  }

  // function to create a lightning effect on a timer
  function weather(_lTimer) {

    lightningAlpha = 0.0;

    if ( _lTimer > 350.0) {
      lightningAlpha = (500.0 - _lTimer) * 0.004;
    }

    else if (_lTimer < 350.0 && _lTimer > 250.0) {
      lightningAlpha = (_lTimer - 250.0) * 0.006;
    }    

    else if (_lTimer < 250.0 && _lTimer >= 100.0) {
      lightningAlpha = (250.0 - _lTimer) * 0.004;
    }

    else if (_lTimer < 100.0 && _lTimer >= 0.0) {
      lightningAlpha = _lTimer * 0.006;
    }

    if (lightningAlpha > 0.0) {
      ctx.fillStyle = 'rgba(250, 250, 245, ' + lightningAlpha + ')';
      ctx.fillRect(0,0,w,h);
    }
  }
});

ZoDoc Clinic

Class Notes

Understanding strings, integers, and booleans.

Source Code

Node.js