Project Introduction
A comprehensive digital clock application with alarm system that displays current time, allows users to set multiple alarms, and provides a fully functional timer and stopwatch. This system includes a beautiful analog and digital clock display, alarm management, snooze functionality, and customizable themes. Perfect for websites needing time management features or as a standalone productivity tool.
✨ Features
Clock Display
- Digital Clock: Real-time updating with multiple formats (12/24 hour)
- Analog Clock: Beautiful SVG-based analog clock with smooth second hand
- Date Display: Current date with day, month, and year
- Timezone Support: Display time in different timezones
- World Clocks: Add multiple clocks for different cities
Alarm System
- Multiple Alarms: Set unlimited alarms with custom labels
- Recurring Alarms: Daily, weekly, or custom recurrence patterns
- Snooze Function: 5, 10, or 15 minute snooze options
- Alarm Sounds: Multiple sound options or custom uploads
- Gradual Volume: Volume increases gradually for gentle waking
- Alarm History: Track triggered alarms
Timer
- Countdown Timer: Set custom durations with presets
- Pause/Resume: Control timer execution
- Multiple Timers: Run several timers simultaneously
- Timer Presets: Quick access to common durations
- Timer Alerts: Visual and sound notifications
Stopwatch
- Precise Timing: Accurate to 1/100 second
- Lap/Split Times: Record and display lap times
- Lap History: View previous lap times
- Export Laps: Download lap data as CSV
Additional Features
- Dark/Light Mode: Toggle between themes
- Fullscreen Mode: Distraction-free display
- Keyboard Shortcuts: Control everything with keyboard
- Sound Customization: Volume control and sound selection
- Data Persistence: Alarms saved in browser localStorage
- Export/Import: Backup and restore alarm settings
📁 File Structure
blog-website/ │ ├── clock/ # Clock application directory │ ├── index.php # Main clock page │ ├── alarms.php # Alarm management │ ├── timer.php # Timer page │ ├── stopwatch.php # Stopwatch page │ ├── settings.php # User settings │ │ │ ├── includes/ # Core includes │ │ ├── clock-config.php # Configuration │ │ ├── clock-functions.php # Core functions │ │ └── alarm-handler.php # Alarm processing │ │ │ ├── css/ # Stylesheets │ │ ├── clock.css # Main clock styles │ │ └── analog-clock.css # Analog clock styles │ │ │ ├── js/ # JavaScript │ │ ├── clock.js # Core clock logic │ │ ├── analog-clock.js # Analog clock renderer │ │ ├── alarms.js # Alarm management │ │ ├── timer.js # Timer functionality │ │ └── stopwatch.js # Stopwatch logic │ │ │ ├── sounds/ # Alarm sounds │ │ ├── alarm-1.mp3 # Default alarm 1 │ │ ├── alarm-2.mp3 # Default alarm 2 │ │ ├── gentle.mp3 # Gentle wake sound │ │ └── notification.mp3 # Notification sound │ │ │ └── assets/ # Images and icons │ ├── clock-face.svg # Analog clock face │ └── icons/ # UI icons │ ├── .env # Environment variables │ └── database/ └── clock.sql # Database schema (optional)
🗄️ Database Schema (database/clock.sql) - Optional
-- Create clock database (optional - for server-side storage)
CREATE DATABASE IF NOT EXISTS clock_db;
USE clock_db;
-- User alarms (if using server-side storage)
CREATE TABLE IF NOT EXISTS user_alarms (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NULL, -- If integrated with user system
alarm_time TIME NOT NULL,
days JSON, -- Days of week [1,2,3,4,5,6,0] (0 = Sunday)
label VARCHAR(255),
sound VARCHAR(100) DEFAULT 'alarm-1.mp3',
is_active BOOLEAN DEFAULT TRUE,
snooze_enabled BOOLEAN DEFAULT TRUE,
snooze_duration INT DEFAULT 5, -- minutes
volume INT DEFAULT 80, -- 0-100
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user (user_id),
INDEX idx_active (is_active)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Alarm history
CREATE TABLE IF NOT EXISTS alarm_history (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
alarm_id INT NULL,
user_id INT NULL,
triggered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
action ENUM('dismissed', 'snoozed', 'triggered') DEFAULT 'triggered',
snooze_count INT DEFAULT 0,
INDEX idx_alarm (alarm_id),
INDEX idx_user (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Timer presets
CREATE TABLE IF NOT EXISTS timer_presets (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NULL,
name VARCHAR(100) NOT NULL,
duration INT NOT NULL, -- in seconds
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Settings
CREATE TABLE IF NOT EXISTS user_settings (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT UNIQUE,
time_format ENUM('12', '24') DEFAULT '12',
theme ENUM('light', 'dark', 'auto') DEFAULT 'auto',
default_sound VARCHAR(100) DEFAULT 'alarm-1.mp3',
default_volume INT DEFAULT 80,
snooze_default INT DEFAULT 5,
show_seconds BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
🔧 Core Files
1. clock/index.php (Main Clock Page)
<?php
require_once '../includes/config.php';
$page_title = 'Digital Clock with Alarm System';
// Get user settings (from localStorage or cookies)
$timeFormat = $_COOKIE['clock_time_format'] ?? '12';
$theme = $_COOKIE['clock_theme'] ?? 'auto';
include '../includes/header.php';
?>
<link rel="stylesheet" href="css/clock.css">
<link rel="stylesheet" href="css/analog-clock.css">
<div class="clock-container" data-theme="<?php echo $theme; ?>">
<!-- Header with Navigation -->
<div class="clock-header">
<nav class="clock-nav">
<a href="index.php" class="nav-link active">Clock</a>
<a href="alarms.php" class="nav-link">Alarms</a>
<a href="timer.php" class="nav-link">Timer</a>
<a href="stopwatch.php" class="nav-link">Stopwatch</a>
<a href="settings.php" class="nav-link">Settings</a>
</nav>
<div class="header-controls">
<button class="theme-toggle" onclick="toggleTheme()" title="Toggle theme">
<span class="theme-icon">🌙</span>
</button>
<button class="fullscreen-toggle" onclick="toggleFullscreen()" title="Fullscreen">
<span class="fullscreen-icon">⛶</span>
</button>
</div>
</div>
<!-- Main Clock Display -->
<div class="clock-main">
<!-- Analog Clock -->
<div class="analog-clock-container">
<svg class="analog-clock" viewBox="0 0 200 200">
<!-- Clock face will be drawn by JavaScript -->
</svg>
</div>
<!-- Digital Clock -->
<div class="digital-clock-container">
<div class="time-display" id="digitalTime">--:--:--</div>
<div class="date-display" id="digitalDate">---, -- --- ----</div>
<!-- Time Format Toggle -->
<div class="format-toggle">
<button class="format-btn <?php echo $timeFormat == '12' ? 'active' : ''; ?>"
onclick="setTimeFormat('12')">12h</button>
<button class="format-btn <?php echo $timeFormat == '24' ? 'active' : ''; ?>"
onclick="setTimeFormat('24')">24h</button>
</div>
</div>
</div>
<!-- World Clocks (optional) -->
<div class="world-clocks">
<h3>World Clocks</h3>
<div class="clocks-grid" id="worldClocks">
<!-- Will be populated by JavaScript -->
</div>
<button class="add-clock-btn" onclick="addWorldClock()">+ Add City</button>
</div>
<!-- Upcoming Alarms -->
<div class="upcoming-alarms">
<h3>Upcoming Alarms</h3>
<div class="alarms-list" id="upcomingAlarms">
<!-- Will be populated by JavaScript -->
</div>
</div>
</div>
<!-- Alarm Notification Modal -->
<div id="alarmModal" class="alarm-modal" style="display: none;">
<div class="alarm-modal-content">
<h2 id="alarmTitle">⏰ Alarm</h2>
<p id="alarmMessage">Time to wake up!</p>
<div class="alarm-actions">
<button class="dismiss-btn" onclick="dismissAlarm()">Dismiss</button>
<button class="snooze-btn" onclick="snoozeAlarm()">Snooze <span id="snoozeTime">5</span> min</button>
</div>
</div>
</div>
<!-- Audio element for alarms -->
<audio id="alarmSound" loop preload="auto">
<source src="sounds/alarm-1.mp3" type="audio/mpeg">
</audio>
<script src="js/clock.js"></script>
<script src="js/analog-clock.js"></script>
<script>
// Initialize clock on page load
document.addEventListener('DOMContentLoaded', function() {
initClock(<?php echo $timeFormat; ?>);
initAnalogClock();
loadAlarms();
loadWorldClocks();
});
// Time format toggle
function setTimeFormat(format) {
document.cookie = `clock_time_format=${format}; path=/; max-age=31536000`;
location.reload();
}
// Theme toggle
function toggleTheme() {
const container = document.querySelector('.clock-container');
const currentTheme = container.dataset.theme;
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
container.dataset.theme = newTheme;
document.cookie = `clock_theme=${newTheme}; path=/; max-age=31536000`;
// Update icon
const icon = document.querySelector('.theme-icon');
icon.textContent = newTheme === 'dark' ? '☀️' : '🌙';
}
// Fullscreen toggle
function toggleFullscreen() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
document.querySelector('.fullscreen-icon').textContent = '✕';
} else {
if (document.exitFullscreen) {
document.exitFullscreen();
document.querySelector('.fullscreen-icon').textContent = '⛶';
}
}
}
// Keyboard shortcuts
document.addEventListener('keydown', function(e) {
// Space: Snooze (if alarm active)
if (e.code === 'Space' && document.getElementById('alarmModal').style.display === 'block') {
e.preventDefault();
snoozeAlarm();
}
// Escape: Dismiss alarm
if (e.code === 'Escape' && document.getElementById('alarmModal').style.display === 'block') {
dismissAlarm();
}
// F: Toggle fullscreen
if (e.code === 'KeyF' && e.ctrlKey) {
e.preventDefault();
toggleFullscreen();
}
// T: Toggle theme
if (e.code === 'KeyT' && e.ctrlKey) {
e.preventDefault();
toggleTheme();
}
});
</script>
<style>
.clock-container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
min-height: 100vh;
transition: background-color 0.3s, color 0.3s;
}
/* Light theme */
.clock-container[data-theme="light"] {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
color: #333;
}
/* Dark theme */
.clock-container[data-theme="dark"] {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
/* Auto theme (follows system) */
@media (prefers-color-scheme: dark) {
.clock-container[data-theme="auto"] {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
}
@media (prefers-color-scheme: light) {
.clock-container[data-theme="auto"] {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
color: #333;
}
}
.clock-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 3rem;
padding: 1rem;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 10px;
}
.clock-nav {
display: flex;
gap: 1rem;
}
.nav-link {
padding: 0.5rem 1rem;
color: inherit;
text-decoration: none;
border-radius: 5px;
transition: background 0.3s;
}
.nav-link:hover {
background: rgba(255, 255, 255, 0.2);
}
.nav-link.active {
background: rgba(255, 255, 255, 0.3);
font-weight: bold;
}
.header-controls {
display: flex;
gap: 0.5rem;
}
.theme-toggle,
.fullscreen-toggle {
background: none;
border: none;
color: inherit;
font-size: 1.2rem;
cursor: pointer;
padding: 0.5rem;
border-radius: 5px;
transition: background 0.3s;
}
.theme-toggle:hover,
.fullscreen-toggle:hover {
background: rgba(255, 255, 255, 0.2);
}
.clock-main {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3rem;
align-items: center;
margin-bottom: 3rem;
}
.analog-clock-container {
display: flex;
justify-content: center;
align-items: center;
}
.analog-clock {
width: 300px;
height: 300px;
}
.digital-clock-container {
text-align: center;
}
.time-display {
font-size: 5rem;
font-weight: bold;
font-family: 'Digital', monospace;
margin-bottom: 1rem;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.date-display {
font-size: 1.5rem;
margin-bottom: 2rem;
opacity: 0.9;
}
.format-toggle {
display: flex;
gap: 1rem;
justify-content: center;
}
.format-btn {
padding: 0.5rem 1.5rem;
background: rgba(255, 255, 255, 0.2);
border: none;
color: inherit;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
}
.format-btn:hover {
background: rgba(255, 255, 255, 0.3);
}
.format-btn.active {
background: rgba(255, 255, 255, 0.4);
font-weight: bold;
}
.world-clocks,
.upcoming-alarms {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 10px;
padding: 1.5rem;
margin-bottom: 2rem;
}
.world-clocks h3,
.upcoming-alarms h3 {
margin-bottom: 1rem;
}
.clocks-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 1rem;
}
.world-clock-card {
background: rgba(255, 255, 255, 0.1);
padding: 1rem;
border-radius: 5px;
text-align: center;
}
.world-clock-card .city {
font-weight: bold;
margin-bottom: 0.5rem;
}
.world-clock-card .time {
font-size: 1.2rem;
font-family: 'Digital', monospace;
}
.world-clock-card .date {
font-size: 0.8rem;
opacity: 0.7;
}
.add-clock-btn {
width: 100%;
padding: 0.5rem;
background: rgba(255, 255, 255, 0.2);
border: 2px dashed rgba(255, 255, 255, 0.3);
color: inherit;
border-radius: 5px;
cursor: pointer;
transition: background 0.3s;
}
.add-clock-btn:hover {
background: rgba(255, 255, 255, 0.3);
}
.alarms-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.alarm-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem;
background: rgba(255, 255, 255, 0.1);
border-radius: 5px;
}
.alarm-info {
flex: 1;
}
.alarm-time {
font-size: 1.2rem;
font-weight: bold;
margin-right: 1rem;
}
.alarm-label {
opacity: 0.9;
}
.alarm-days {
font-size: 0.8rem;
opacity: 0.7;
}
.alarm-switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.alarm-switch input {
opacity: 0;
width: 0;
height: 0;
}
.alarm-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, 0.3);
transition: 0.3s;
border-radius: 24px;
}
.alarm-slider:before {
position: absolute;
content: "";
height: 20px;
width: 20px;
left: 2px;
bottom: 2px;
background-color: white;
transition: 0.3s;
border-radius: 50%;
}
input:checked + .alarm-slider {
background-color: #4CAF50;
}
input:checked + .alarm-slider:before {
transform: translateX(26px);
}
/* Alarm Modal */
.alarm-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.8);
z-index: 2000;
display: flex;
align-items: center;
justify-content: center;
}
.alarm-modal-content {
background: white;
padding: 2rem;
border-radius: 10px;
max-width: 400px;
width: 90%;
text-align: center;
animation: pulse 1s infinite;
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.alarm-modal-content h2 {
color: #333;
margin-bottom: 1rem;
font-size: 2rem;
}
.alarm-modal-content p {
color: #666;
margin-bottom: 2rem;
font-size: 1.2rem;
}
.alarm-actions {
display: flex;
gap: 1rem;
justify-content: center;
}
.dismiss-btn,
.snooze-btn {
padding: 0.8rem 2rem;
border: none;
border-radius: 5px;
font-size: 1rem;
cursor: pointer;
transition: opacity 0.3s;
}
.dismiss-btn {
background: #dc3545;
color: white;
}
.snooze-btn {
background: #4CAF50;
color: white;
}
.dismiss-btn:hover,
.snooze-btn:hover {
opacity: 0.9;
}
/* Responsive */
@media (max-width: 768px) {
.clock-main {
grid-template-columns: 1fr;
gap: 2rem;
}
.time-display {
font-size: 3rem;
}
.date-display {
font-size: 1.2rem;
}
.analog-clock {
width: 250px;
height: 250px;
}
.clock-nav {
flex-wrap: wrap;
}
}
</style>
<?php include '../includes/footer.php'; ?>
2. js/clock.js (Core Clock Logic)
/**
* Core Clock Functionality
*/
let clockInterval;
let timeFormat = 12; // 12 or 24
let currentTime = new Date();
let alarms = [];
let activeAlarm = null;
let snoozeTimeout = null;
// Initialize clock
function initClock(format) {
timeFormat = format;
updateClock();
startClock();
loadAlarmsFromStorage();
checkAlarms();
}
// Start clock updates
function startClock() {
if (clockInterval) {
clearInterval(clockInterval);
}
clockInterval = setInterval(updateClock, 1000);
}
// Stop clock updates
function stopClock() {
if (clockInterval) {
clearInterval(clockInterval);
clockInterval = null;
}
}
// Update clock display
function updateClock() {
currentTime = new Date();
// Update digital clock
updateDigitalClock();
// Update analog clock
if (typeof updateAnalogClock === 'function') {
updateAnalogClock(currentTime);
}
// Update world clocks
updateWorldClocks();
// Check alarms every second
checkAlarms();
}
// Update digital clock display
function updateDigitalClock() {
const timeElement = document.getElementById('digitalTime');
const dateElement = document.getElementById('digitalDate');
if (!timeElement || !dateElement) return;
// Format time
let hours = currentTime.getHours();
let minutes = currentTime.getMinutes();
let seconds = currentTime.getSeconds();
let ampm = '';
if (timeFormat === 12) {
ampm = hours >= 12 ? ' PM' : ' AM';
hours = hours % 12 || 12;
}
const timeString = `${padZero(hours)}:${padZero(minutes)}:${padZero(seconds)}${ampm}`;
// Format date
const options = {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
};
const dateString = currentTime.toLocaleDateString(undefined, options);
timeElement.textContent = timeString;
dateElement.textContent = dateString;
}
// Pad zero helper
function padZero(num) {
return num.toString().padStart(2, '0');
}
// Alarm Management
function loadAlarmsFromStorage() {
const stored = localStorage.getItem('alarms');
if (stored) {
try {
alarms = JSON.parse(stored);
updateAlarmsList();
} catch (e) {
console.error('Failed to load alarms', e);
alarms = [];
}
}
}
function saveAlarmsToStorage() {
localStorage.setItem('alarms', JSON.stringify(alarms));
updateAlarmsList();
}
function addAlarm(alarm) {
// Generate ID
alarm.id = Date.now() + Math.random().toString(36).substr(2, 9);
// Ensure all fields
alarm.time = alarm.time || '08:00';
alarm.days = alarm.days || [1,2,3,4,5]; // Monday-Friday by default
alarm.label = alarm.label || 'Alarm';
alarm.sound = alarm.sound || 'alarm-1.mp3';
alarm.isActive = alarm.isActive !== false;
alarm.snoozeEnabled = alarm.snoozeEnabled !== false;
alarm.snoozeDuration = alarm.snoozeDuration || 5;
alarm.volume = alarm.volume || 80;
alarms.push(alarm);
saveAlarmsToStorage();
return alarm.id;
}
function updateAlarm(id, updates) {
const index = alarms.findIndex(a => a.id === id);
if (index !== -1) {
alarms[index] = { ...alarms[index], ...updates };
saveAlarmsToStorage();
}
}
function deleteAlarm(id) {
alarms = alarms.filter(a => a.id !== id);
saveAlarmsToStorage();
}
function toggleAlarm(id) {
const alarm = alarms.find(a => a.id === id);
if (alarm) {
alarm.isActive = !alarm.isActive;
saveAlarmsToStorage();
}
}
// Update alarms list in UI
function updateAlarmsList() {
const container = document.getElementById('upcomingAlarms');
if (!container) return;
if (alarms.length === 0) {
container.innerHTML = '<p class="no-alarms">No alarms set</p>';
return;
}
// Sort alarms by time
const sorted = [...alarms].sort((a, b) => {
return a.time.localeCompare(b.time);
});
let html = '';
sorted.forEach(alarm => {
const [hours, minutes] = alarm.time.split(':');
const time12 = formatTime12(parseInt(hours), parseInt(minutes));
html += `
<div class="alarm-item">
<div class="alarm-info">
<span class="alarm-time">${time12}</span>
<span class="alarm-label">${escapeHtml(alarm.label)}</span>
<div class="alarm-days">${formatDays(alarm.days)}</div>
</div>
<label class="alarm-switch">
<input type="checkbox" ${alarm.isActive ? 'checked' : ''}
onchange="toggleAlarm('${alarm.id}')">
<span class="alarm-slider"></span>
</label>
</div>
`;
});
container.innerHTML = html;
}
// Format days for display
function formatDays(days) {
if (!days || days.length === 0) return 'Once';
if (days.length === 7) return 'Every day';
const dayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const sorted = days.sort((a, b) => a - b);
// Check for weekdays
if (days.length === 5 && days.includes(1) && days.includes(2) &&
days.includes(3) && days.includes(4) && days.includes(5)) {
return 'Weekdays';
}
// Check for weekends
if (days.length === 2 && days.includes(0) && days.includes(6)) {
return 'Weekends';
}
return sorted.map(d => dayNames[d]).join(', ');
}
// Format time in 12-hour format
function formatTime12(hours, minutes) {
const ampm = hours >= 12 ? 'PM' : 'AM';
const h = hours % 12 || 12;
return `${h}:${padZero(minutes)} ${ampm}`;
}
// Check if alarm should trigger
function checkAlarms() {
const now = new Date();
const currentHours = now.getHours();
const currentMinutes = now.getMinutes();
const currentDay = now.getDay(); // 0 = Sunday
alarms.forEach(alarm => {
if (!alarm.isActive) return;
const [alarmHours, alarmMinutes] = alarm.time.split(':').map(Number);
// Check if time matches
if (alarmHours === currentHours && alarmMinutes === currentMinutes) {
// Check if day matches (if days specified)
if (alarm.days && alarm.days.length > 0) {
if (!alarm.days.includes(currentDay)) {
return;
}
}
// Trigger alarm
triggerAlarm(alarm);
}
});
}
// Trigger alarm
function triggerAlarm(alarm) {
// Don't trigger if already active
if (activeAlarm) return;
activeAlarm = alarm;
// Show modal
const modal = document.getElementById('alarmModal');
const title = document.getElementById('alarmTitle');
const message = document.getElementById('alarmMessage');
const snoozeSpan = document.getElementById('snoozeTime');
title.textContent = `⏰ ${alarm.label}`;
message.textContent = `It's ${alarm.time}`;
snoozeSpan.textContent = alarm.snoozeDuration;
modal.style.display = 'flex';
// Play sound
playAlarmSound(alarm);
// Log alarm trigger
logAlarmTrigger(alarm.id);
}
// Play alarm sound
function playAlarmSound(alarm) {
const audio = document.getElementById('alarmSound');
// Set sound source
audio.src = `sounds/${alarm.sound}`;
// Set volume
audio.volume = alarm.volume / 100;
// Play with gradual volume increase
let currentVolume = 0.1;
audio.volume = currentVolume;
audio.play().catch(e => {
console.error('Failed to play alarm sound', e);
});
// Gradually increase volume
const interval = setInterval(() => {
if (currentVolume < alarm.volume / 100) {
currentVolume += 0.1;
audio.volume = Math.min(currentVolume, alarm.volume / 100);
} else {
clearInterval(interval);
}
}, 1000);
// Store interval to clear later
audio._volumeInterval = interval;
}
// Stop alarm sound
function stopAlarmSound() {
const audio = document.getElementById('alarmSound');
audio.pause();
audio.currentTime = 0;
if (audio._volumeInterval) {
clearInterval(audio._volumeInterval);
}
}
// Dismiss alarm
function dismissAlarm() {
stopAlarmSound();
const modal = document.getElementById('alarmModal');
modal.style.display = 'none';
// Log dismissal
if (activeAlarm) {
logAlarmAction(activeAlarm.id, 'dismissed');
activeAlarm = null;
}
// Clear any snooze timeout
if (snoozeTimeout) {
clearTimeout(snoozeTimeout);
snoozeTimeout = null;
}
}
// Snooze alarm
function snoozeAlarm() {
if (!activeAlarm) return;
stopAlarmSound();
const modal = document.getElementById('alarmModal');
modal.style.display = 'none';
const snoozeMinutes = activeAlarm.snoozeDuration || 5;
// Set snooze timeout
snoozeTimeout = setTimeout(() => {
triggerAlarm(activeAlarm);
snoozeTimeout = null;
}, snoozeMinutes * 60 * 1000);
// Log snooze
logAlarmAction(activeAlarm.id, 'snoozed', snoozeMinutes);
// Show snooze notification
showNotification(`Alarm snoozed for ${snoozeMinutes} minutes`);
}
// Show notification
function showNotification(message) {
// Check if browser supports notifications
if (!("Notification" in window)) return;
if (Notification.permission === "granted") {
new Notification("Clock Alarm", { body: message });
} else if (Notification.permission !== "denied") {
Notification.requestPermission().then(permission => {
if (permission === "granted") {
new Notification("Clock Alarm", { body: message });
}
});
}
}
// Log alarm trigger (for analytics)
function logAlarmTrigger(alarmId) {
console.log('Alarm triggered:', alarmId);
// Could send to server if needed
}
function logAlarmAction(alarmId, action, snoozeMinutes) {
console.log('Alarm action:', { alarmId, action, snoozeMinutes });
// Could send to server if needed
}
// World Clocks
const worldClocks = [
{ city: 'New York', timezone: 'America/New_York' },
{ city: 'London', timezone: 'Europe/London' },
{ city: 'Tokyo', timezone: 'Asia/Tokyo' },
{ city: 'Sydney', timezone: 'Australia/Sydney' }
];
function loadWorldClocks() {
const container = document.getElementById('worldClocks');
if (!container) return;
updateWorldClocks();
}
function updateWorldClocks() {
const container = document.getElementById('worldClocks');
if (!container) return;
let html = '';
worldClocks.forEach(clock => {
try {
const time = new Date().toLocaleString('en-US', {
timeZone: clock.timezone,
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: timeFormat === 12
});
const date = new Date().toLocaleString('en-US', {
timeZone: clock.timezone,
weekday: 'short',
month: 'short',
day: 'numeric'
});
html += `
<div class="world-clock-card">
<div class="city">${clock.city}</div>
<div class="time">${time}</div>
<div class="date">${date}</div>
</div>
`;
} catch (e) {
console.error('Error with timezone:', clock.timezone);
}
});
container.innerHTML = html;
}
function addWorldClock() {
const city = prompt('Enter city name:');
const timezone = prompt('Enter timezone (e.g., America/New_York):');
if (city && timezone) {
worldClocks.push({ city, timezone });
updateWorldClocks();
}
}
// Utility function to escape HTML
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Request notification permission on load
if ("Notification" in window) {
if (Notification.permission === "default") {
Notification.requestPermission();
}
}
3. js/analog-clock.js (Analog Clock Renderer)
/**
* Analog Clock SVG Renderer
*/
let clockSvg;
let hourHand, minuteHand, secondHand;
let clockFace;
function initAnalogClock() {
clockSvg = document.querySelector('.analog-clock');
if (!clockSvg) return;
// Clear any existing content
clockSvg.innerHTML = '';
// Draw clock face
drawClockFace();
// Draw hands
drawClockHands();
// Initial update
updateAnalogClock(new Date());
}
function drawClockFace() {
const svgNS = "http://www.w3.org/2000/svg";
const centerX = 100;
const centerY = 100;
const radius = 90;
// Outer circle
const circle = document.createElementNS(svgNS, "circle");
circle.setAttribute("cx", centerX);
circle.setAttribute("cy", centerY);
circle.setAttribute("r", radius);
circle.setAttribute("fill", "white");
circle.setAttribute("stroke", "#333");
circle.setAttribute("stroke-width", "2");
clockSvg.appendChild(circle);
// Hour markers
for (let i = 0; i < 12; i++) {
const angle = (i * 30 - 90) * Math.PI / 180;
const x1 = centerX + (radius - 15) * Math.cos(angle);
const y1 = centerY + (radius - 15) * Math.sin(angle);
const x2 = centerX + (radius - 5) * Math.cos(angle);
const y2 = centerY + (radius - 5) * Math.sin(angle);
const line = document.createElementNS(svgNS, "line");
line.setAttribute("x1", x1);
line.setAttribute("y1", y1);
line.setAttribute("x2", x2);
line.setAttribute("y2", y2);
line.setAttribute("stroke", "#333");
line.setAttribute("stroke-width", i % 3 === 0 ? "3" : "1");
clockSvg.appendChild(line);
// Add numbers for 12, 3, 6, 9
if (i === 0) {
const text = document.createElementNS(svgNS, "text");
text.setAttribute("x", centerX - 5);
text.setAttribute("y", centerY - radius + 20);
text.setAttribute("text-anchor", "middle");
text.setAttribute("font-size", "14");
text.setAttribute("fill", "#333");
text.textContent = "12";
clockSvg.appendChild(text);
} else if (i === 3) {
const text = document.createElementNS(svgNS, "text");
text.setAttribute("x", centerX + radius - 25);
text.setAttribute("y", centerY + 5);
text.setAttribute("text-anchor", "middle");
text.setAttribute("font-size", "14");
text.setAttribute("fill", "#333");
text.textContent = "3";
clockSvg.appendChild(text);
} else if (i === 6) {
const text = document.createElementNS(svgNS, "text");
text.setAttribute("x", centerX);
text.setAttribute("y", centerY + radius - 15);
text.setAttribute("text-anchor", "middle");
text.setAttribute("font-size", "14");
text.setAttribute("fill", "#333");
text.textContent = "6";
clockSvg.appendChild(text);
} else if (i === 9) {
const text = document.createElementNS(svgNS, "text");
text.setAttribute("x", centerX - radius + 25);
text.setAttribute("y", centerY + 5);
text.setAttribute("text-anchor", "middle");
text.setAttribute("font-size", "14");
text.setAttribute("fill", "#333");
text.textContent = "9";
clockSvg.appendChild(text);
}
}
// Center dot
const dot = document.createElementNS(svgNS, "circle");
dot.setAttribute("cx", centerX);
dot.setAttribute("cy", centerY);
dot.setAttribute("r", "5");
dot.setAttribute("fill", "#333");
clockSvg.appendChild(dot);
}
function drawClockHands() {
const svgNS = "http://www.w3.org/2000/svg";
// Hour hand
hourHand = document.createElementNS(svgNS, "line");
hourHand.setAttribute("x1", "100");
hourHand.setAttribute("y1", "100");
hourHand.setAttribute("x2", "100");
hourHand.setAttribute("y2", "100");
hourHand.setAttribute("stroke", "#333");
hourHand.setAttribute("stroke-width", "4");
hourHand.setAttribute("stroke-linecap", "round");
clockSvg.appendChild(hourHand);
// Minute hand
minuteHand = document.createElementNS(svgNS, "line");
minuteHand.setAttribute("x1", "100");
minuteHand.setAttribute("y1", "100");
minuteHand.setAttribute("x2", "100");
minuteHand.setAttribute("y2", "100");
minuteHand.setAttribute("stroke", "#333");
minuteHand.setAttribute("stroke-width", "3");
minuteHand.setAttribute("stroke-linecap", "round");
clockSvg.appendChild(minuteHand);
// Second hand
secondHand = document.createElementNS(svgNS, "line");
secondHand.setAttribute("x1", "100");
secondHand.setAttribute("y1", "100");
secondHand.setAttribute("x2", "100");
secondHand.setAttribute("y2", "100");
secondHand.setAttribute("stroke", "#f00");
secondHand.setAttribute("stroke-width", "2");
secondHand.setAttribute("stroke-linecap", "round");
clockSvg.appendChild(secondHand);
}
function updateAnalogClock(time) {
if (!hourHand || !minuteHand || !secondHand) return;
const hours = time.getHours() % 12;
const minutes = time.getMinutes();
const seconds = time.getSeconds();
const milliseconds = time.getMilliseconds();
// Calculate angles (in radians from 12 o'clock)
const hourAngle = (hours * 30 + minutes * 0.5) * Math.PI / 180 - Math.PI / 2;
const minuteAngle = (minutes * 6 + seconds * 0.1) * Math.PI / 180 - Math.PI / 2;
const secondAngle = (seconds * 6 + milliseconds * 0.006) * Math.PI / 180 - Math.PI / 2;
// Hand lengths
const hourLength = 50;
const minuteLength = 70;
const secondLength = 80;
// Update hour hand
const hourX = 100 + hourLength * Math.cos(hourAngle);
const hourY = 100 + hourLength * Math.sin(hourAngle);
hourHand.setAttribute("x2", hourX);
hourHand.setAttribute("y2", hourY);
// Update minute hand
const minuteX = 100 + minuteLength * Math.cos(minuteAngle);
const minuteY = 100 + minuteLength * Math.sin(minuteAngle);
minuteHand.setAttribute("x2", minuteX);
minuteHand.setAttribute("y2", minuteY);
// Update second hand
const secondX = 100 + secondLength * Math.cos(secondAngle);
const secondY = 100 + secondLength * Math.sin(secondAngle);
secondHand.setAttribute("x2", secondX);
secondHand.setAttribute("y2", secondY);
}
4. clock/timer.php (Timer Page)
<?php
require_once '../includes/config.php';
$page_title = 'Timer - Digital Clock';
include '../includes/header.php';
?>
<link rel="stylesheet" href="css/clock.css">
<div class="timer-container">
<div class="clock-header">
<nav class="clock-nav">
<a href="index.php" class="nav-link">Clock</a>
<a href="alarms.php" class="nav-link">Alarms</a>
<a href="timer.php" class="nav-link active">Timer</a>
<a href="stopwatch.php" class="nav-link">Stopwatch</a>
<a href="settings.php" class="nav-link">Settings</a>
</nav>
</div>
<div class="timer-main">
<h1>Timer</h1>
<!-- Timer Display -->
<div class="timer-display" id="timerDisplay">00:00:00</div>
<!-- Timer Input -->
<div class="timer-input" id="timerInput">
<div class="input-group">
<input type="number" id="hours" min="0" max="23" value="0" placeholder="HH">
<span>:</span>
<input type="number" id="minutes" min="0" max="59" value="0" placeholder="MM">
<span>:</span>
<input type="number" id="seconds" min="0" max="59" value="0" placeholder="SS">
</div>
</div>
<!-- Timer Controls -->
<div class="timer-controls">
<button class="timer-btn start" onclick="startTimer()" id="startBtn">Start</button>
<button class="timer-btn pause" onclick="pauseTimer()" id="pauseBtn" style="display: none;">Pause</button>
<button class="timer-btn reset" onclick="resetTimer()" id="resetBtn">Reset</button>
</div>
<!-- Preset Times -->
<div class="preset-times">
<h3>Quick Presets</h3>
<div class="preset-buttons">
<button onclick="setTimer(0,1,0)">1 min</button>
<button onclick="setTimer(0,5,0)">5 min</button>
<button onclick="setTimer(0,10,0)">10 min</button>
<button onclick="setTimer(0,15,0)">15 min</button>
<button onclick="setTimer(0,30,0)">30 min</button>
<button onclick="setTimer(1,0,0)">1 hour</button>
</div>
</div>
<!-- Multiple Timers -->
<div class="multiple-timers">
<h3>Multiple Timers</h3>
<button class="add-timer-btn" onclick="addNewTimer()">+ Add Timer</button>
<div id="timersList" class="timers-list"></div>
</div>
</div>
</div>
<!-- Timer Complete Modal -->
<div id="timerCompleteModal" class="alarm-modal" style="display: none;">
<div class="alarm-modal-content">
<h2>⏰ Timer Complete!</h2>
<p id="timerCompleteMessage">Time's up!</p>
<div class="alarm-actions">
<button class="dismiss-btn" onclick="dismissTimerComplete()">OK</button>
</div>
</div>
</div>
<audio id="timerSound" preload="auto">
<source src="sounds/notification.mp3" type="audio/mpeg">
</audio>
<script src="js/timer.js"></script>
<style>
.timer-container {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}
.timer-main {
text-align: center;
}
.timer-main h1 {
font-size: 2.5rem;
margin-bottom: 2rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.timer-display {
font-size: 5rem;
font-family: 'Digital', monospace;
margin-bottom: 2rem;
color: #333;
}
.timer-input {
margin-bottom: 2rem;
}
.input-group {
display: flex;
justify-content: center;
align-items: center;
gap: 0.5rem;
}
.input-group input {
width: 80px;
padding: 0.5rem;
font-size: 1.5rem;
text-align: center;
border: 2px solid #ddd;
border-radius: 5px;
}
.input-group span {
font-size: 2rem;
font-weight: bold;
color: #666;
}
.timer-controls {
display: flex;
gap: 1rem;
justify-content: center;
margin-bottom: 3rem;
}
.timer-btn {
padding: 1rem 2rem;
font-size: 1.2rem;
border: none;
border-radius: 5px;
cursor: pointer;
transition: opacity 0.3s;
}
.timer-btn.start {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.timer-btn.pause {
background: #ffc107;
color: #333;
}
.timer-btn.reset {
background: #6c757d;
color: white;
}
.timer-btn:hover {
opacity: 0.9;
}
.preset-times {
margin-bottom: 3rem;
}
.preset-times h3 {
margin-bottom: 1rem;
color: #333;
}
.preset-buttons {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
justify-content: center;
}
.preset-buttons button {
padding: 0.5rem 1rem;
background: #f8f9fa;
border: 1px solid #ddd;
border-radius: 5px;
cursor: pointer;
transition: all 0.3s;
}
.preset-buttons button:hover {
background: #e9ecef;
transform: translateY(-2px);
}
.multiple-timers {
margin-top: 3rem;
}
.multiple-timers h3 {
margin-bottom: 1rem;
color: #333;
}
.add-timer-btn {
padding: 0.5rem 1rem;
background: #28a745;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
margin-bottom: 1rem;
}
.add-timer-btn:hover {
opacity: 0.9;
}
.timers-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
}
.timer-card {
background: white;
border-radius: 5px;
padding: 1rem;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
position: relative;
}
.timer-card .timer-name {
font-weight: bold;
margin-bottom: 0.5rem;
}
.timer-card .timer-time {
font-size: 1.2rem;
font-family: 'Digital', monospace;
margin-bottom: 0.5rem;
}
.timer-card .timer-status {
font-size: 0.9rem;
color: #666;
}
.timer-card .delete-timer {
position: absolute;
top: 0.5rem;
right: 0.5rem;
background: none;
border: none;
color: #dc3545;
cursor: pointer;
font-size: 1.2rem;
}
</style>
<?php include '../includes/footer.php'; ?>
5. js/timer.js (Timer Functionality)
/**
* Timer Functionality
*/
let mainTimer = {
hours: 0,
minutes: 0,
seconds: 0,
totalSeconds: 0,
remainingSeconds: 0,
interval: null,
isRunning: false
};
let multipleTimers = [];
let nextTimerId = 1;
// Initialize timer
function initTimer() {
resetTimer();
}
// Set timer from inputs
function setTimer(hours, minutes, seconds) {
document.getElementById('hours').value = hours;
document.getElementById('minutes').value = minutes;
document.getElementById('seconds').value = seconds;
mainTimer.hours = hours;
mainTimer.minutes = minutes;
mainTimer.seconds = seconds;
mainTimer.totalSeconds = hours * 3600 + minutes * 60 + seconds;
mainTimer.remainingSeconds = mainTimer.totalSeconds;
updateTimerDisplay();
}
// Start main timer
function startTimer() {
if (mainTimer.totalSeconds === 0) {
// Get values from inputs
const hours = parseInt(document.getElementById('hours').value) || 0;
const minutes = parseInt(document.getElementById('minutes').value) || 0;
const seconds = parseInt(document.getElementById('seconds').value) || 0;
setTimer(hours, minutes, seconds);
}
if (mainTimer.totalSeconds === 0) {
alert('Please set a time');
return;
}
if (mainTimer.isRunning) return;
mainTimer.isRunning = true;
// Update UI
document.getElementById('startBtn').style.display = 'none';
document.getElementById('pauseBtn').style.display = 'inline-block';
document.getElementById('timerInput').style.display = 'none';
mainTimer.interval = setInterval(() => {
if (mainTimer.remainingSeconds > 0) {
mainTimer.remainingSeconds--;
updateTimerDisplay();
} else {
completeTimer();
}
}, 1000);
}
// Pause main timer
function pauseTimer() {
if (!mainTimer.isRunning) return;
clearInterval(mainTimer.interval);
mainTimer.isRunning = false;
// Update UI
document.getElementById('startBtn').style.display = 'inline-block';
document.getElementById('pauseBtn').style.display = 'none';
}
// Reset main timer
function resetTimer() {
pauseTimer();
mainTimer.hours = 0;
mainTimer.minutes = 0;
mainTimer.seconds = 0;
mainTimer.totalSeconds = 0;
mainTimer.remainingSeconds = 0;
// Update UI
document.getElementById('startBtn').style.display = 'inline-block';
document.getElementById('pauseBtn').style.display = 'none';
document.getElementById('timerInput').style.display = 'block';
document.getElementById('hours').value = 0;
document.getElementById('minutes').value = 0;
document.getElementById('seconds').value = 0;
updateTimerDisplay();
}
// Complete timer
function completeTimer() {
pauseTimer();
// Play sound
const audio = document.getElementById('timerSound');
audio.play();
// Show modal
const modal = document.getElementById('timerCompleteModal');
const message = document.getElementById('timerCompleteMessage');
message.textContent = `${mainTimer.hours}h ${mainTimer.minutes}m ${mainTimer.seconds}s timer completed!`;
modal.style.display = 'flex';
// Auto hide after 5 seconds
setTimeout(() => {
modal.style.display = 'none';
}, 5000);
}
// Dismiss timer complete modal
function dismissTimerComplete() {
document.getElementById('timerCompleteModal').style.display = 'none';
}
// Update timer display
function updateTimerDisplay() {
const display = document.getElementById('timerDisplay');
const hours = Math.floor(mainTimer.remainingSeconds / 3600);
const minutes = Math.floor((mainTimer.remainingSeconds % 3600) / 60);
const seconds = mainTimer.remainingSeconds % 60;
display.textContent = `${padZero(hours)}:${padZero(minutes)}:${padZero(seconds)}`;
// Update document title
document.title = `${padZero(hours)}:${padZero(minutes)}:${padZero(seconds)} - Timer`;
}
// Add new multiple timer
function addNewTimer() {
const timer = {
id: nextTimerId++,
name: `Timer ${nextTimerId - 1}`,
hours: 0,
minutes: 0,
seconds: 0,
totalSeconds: 0,
remainingSeconds: 0,
interval: null,
isRunning: false
};
multipleTimers.push(timer);
updateMultipleTimersUI();
}
// Delete multiple timer
function deleteTimer(id) {
const index = multipleTimers.findIndex(t => t.id === id);
if (index !== -1) {
const timer = multipleTimers[index];
if (timer.interval) {
clearInterval(timer.interval);
}
multipleTimers.splice(index, 1);
updateMultipleTimersUI();
}
}
// Start multiple timer
function startMultipleTimer(id) {
const timer = multipleTimers.find(t => t.id === id);
if (!timer || timer.isRunning) return;
if (timer.totalSeconds === 0) {
// Get values from inputs (you'd need to create input fields in the UI)
return;
}
timer.isRunning = true;
timer.interval = setInterval(() => {
if (timer.remainingSeconds > 0) {
timer.remainingSeconds--;
updateMultipleTimerDisplay(id);
} else {
completeMultipleTimer(id);
}
}, 1000);
updateMultipleTimersUI();
}
// Pause multiple timer
function pauseMultipleTimer(id) {
const timer = multipleTimers.find(t => t.id === id);
if (!timer || !timer.isRunning) return;
clearInterval(timer.interval);
timer.isRunning = false;
updateMultipleTimersUI();
}
// Reset multiple timer
function resetMultipleTimer(id) {
const timer = multipleTimers.find(t => t.id === id);
if (!timer) return;
if (timer.interval) {
clearInterval(timer.interval);
}
timer.isRunning = false;
timer.remainingSeconds = timer.totalSeconds;
updateMultipleTimerDisplay(id);
updateMultipleTimersUI();
}
// Complete multiple timer
function completeMultipleTimer(id) {
const timer = multipleTimers.find(t => t.id === id);
if (!timer) return;
pauseMultipleTimer(id);
// Play sound
const audio = document.getElementById('timerSound');
audio.play();
// Show notification
showNotification(`Timer "${timer.name}" completed!`);
}
// Update multiple timer display
function updateMultipleTimerDisplay(id) {
const timer = multipleTimers.find(t => t.id === id);
if (!timer) return;
const element = document.getElementById(`timer-${id}`);
if (element) {
const hours = Math.floor(timer.remainingSeconds / 3600);
const minutes = Math.floor((timer.remainingSeconds % 3600) / 60);
const seconds = timer.remainingSeconds % 60;
element.querySelector('.timer-time').textContent =
`${padZero(hours)}:${padZero(minutes)}:${padZero(seconds)}`;
}
}
// Update multiple timers UI
function updateMultipleTimersUI() {
const container = document.getElementById('timersList');
if (!container) return;
if (multipleTimers.length === 0) {
container.innerHTML = '<p class="no-timers">No additional timers</p>';
return;
}
let html = '';
multipleTimers.forEach(timer => {
const hours = Math.floor(timer.remainingSeconds / 3600);
const minutes = Math.floor((timer.remainingSeconds % 3600) / 60);
const seconds = timer.remainingSeconds % 60;
html += `
<div class="timer-card" id="timer-${timer.id}">
<button class="delete-timer" onclick="deleteTimer(${timer.id})">✕</button>
<div class="timer-name">${escapeHtml(timer.name)}</div>
<div class="timer-time">${padZero(hours)}:${padZero(minutes)}:${padZero(seconds)}</div>
<div class="timer-status">${timer.isRunning ? 'Running' : 'Stopped'}</div>
<div class="timer-controls">
${!timer.isRunning ?
`<button onclick="startMultipleTimer(${timer.id})">Start</button>` :
`<button onclick="pauseMultipleTimer(${timer.id})">Pause</button>`
}
<button onclick="resetMultipleTimer(${timer.id})">Reset</button>
</div>
</div>
`;
});
container.innerHTML = html;
}
// Pad zero helper (reused from clock.js)
function padZero(num) {
return num.toString().padStart(2, '0');
}
// Initialize on page load
document.addEventListener('DOMContentLoaded', initTimer);
6. clock/alarms.php (Alarm Management Page)
<?php
require_once '../includes/config.php';
$page_title = 'Alarms - Digital Clock';
include '../includes/header.php';
?>
<link rel="stylesheet" href="css/clock.css">
<div class="alarms-container">
<div class="clock-header">
<nav class="clock-nav">
<a href="index.php" class="nav-link">Clock</a>
<a href="alarms.php" class="nav-link active">Alarms</a>
<a href="timer.php" class="nav-link">Timer</a>
<a href="stopwatch.php" class="nav-link">Stopwatch</a>
<a href="settings.php" class="nav-link">Settings</a>
</nav>
</div>
<div class="alarms-main">
<h1>Alarms</h1>
<!-- Add Alarm Button -->
<button class="add-alarm-btn" onclick="showAddAlarmModal()">+ Add New Alarm</button>
<!-- Alarms List -->
<div class="alarms-list-container" id="alarmsList">
<!-- Alarms will be loaded here by JavaScript -->
</div>
</div>
</div>
<!-- Add/Edit Alarm Modal -->
<div id="alarmModal" class="modal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h2 id="modalTitle">Add Alarm</h2>
<span class="close" onclick="closeAlarmModal()">×</span>
</div>
<div class="modal-body">
<form id="alarmForm" onsubmit="saveAlarm(event)">
<input type="hidden" id="alarmId" value="">
<div class="form-group">
<label for="alarmTime">Time</label>
<input type="time" id="alarmTime" class="form-control" required>
</div>
<div class="form-group">
<label for="alarmLabel">Label</label>
<input type="text" id="alarmLabel" class="form-control" placeholder="Wake up" value="Alarm">
</div>
<div class="form-group">
<label>Repeat</label>
<div class="days-selector">
<label class="day-checkbox">
<input type="checkbox" name="days[]" value="0"> Sun
</label>
<label class="day-checkbox">
<input type="checkbox" name="days[]" value="1" checked> Mon
</label>
<label class="day-checkbox">
<input type="checkbox" name="days[]" value="2" checked> Tue
</label>
<label class="day-checkbox">
<input type="checkbox" name="days[]" value="3" checked> Wed
</label>
<label class="day-checkbox">
<input type="checkbox" name="days[]" value="4" checked> Thu
</label>
<label class="day-checkbox">
<input type="checkbox" name="days[]" value="5" checked> Fri
</label>
<label class="day-checkbox">
<input type="checkbox" name="days[]" value="6"> Sat
</label>
</div>
</div>
<div class="form-group">
<label for="alarmSound">Sound</label>
<select id="alarmSound" class="form-control">
<option value="alarm-1.mp3">Classic Alarm</option>
<option value="alarm-2.mp3">Digital Beep</option>
<option value="gentle.mp3">Gentle Wake</option>
<option value="notification.mp3">Notification</option>
</select>
</div>
<div class="form-group">
<label for="alarmVolume">Volume</label>
<input type="range" id="alarmVolume" min="0" max="100" value="80" class="volume-slider">
<span id="volumeValue">80%</span>
</div>
<div class="form-group">
<label>
<input type="checkbox" id="snoozeEnabled" checked>
Enable Snooze
</label>
</div>
<div class="form-group" id="snoozeGroup">
<label for="snoozeDuration">Snooze Duration (minutes)</label>
<select id="snoozeDuration" class="form-control">
<option value="5">5 minutes</option>
<option value="10">10 minutes</option>
<option value="15">15 minutes</option>
</select>
</div>
<div class="form-actions">
<button type="submit" class="btn-save">Save Alarm</button>
<button type="button" class="btn-cancel" onclick="closeAlarmModal()">Cancel</button>
</div>
</form>
</div>
</div>
</div>
<script src="js/clock.js"></script>
<script src="js/alarms.js"></script>
<style>
.alarms-container {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}
.alarms-main {
text-align: center;
}
.alarms-main h1 {
font-size: 2.5rem;
margin-bottom: 2rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.add-alarm-btn {
padding: 1rem 2rem;
font-size: 1.1rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
margin-bottom: 2rem;
transition: opacity 0.3s;
}
.add-alarm-btn:hover {
opacity: 0.9;
}
.alarms-list-container {
background: white;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
padding: 1rem;
}
.alarm-item {
display: flex;
align-items: center;
padding: 1rem;
border-bottom: 1px solid #eee;
transition: background 0.3s;
}
.alarm-item:hover {
background: #f8f9fa;
}
.alarm-item:last-child {
border-bottom: none;
}
.alarm-time {
font-size: 1.5rem;
font-weight: bold;
width: 100px;
}
.alarm-details {
flex: 1;
text-align: left;
}
.alarm-label {
font-size: 1.1rem;
margin-bottom: 0.3rem;
}
.alarm-schedule {
font-size: 0.9rem;
color: #666;
}
.alarm-actions {
display: flex;
gap: 0.5rem;
}
.alarm-edit,
.alarm-delete {
padding: 0.3rem 0.8rem;
border: none;
border-radius: 3px;
cursor: pointer;
transition: opacity 0.3s;
}
.alarm-edit {
background: #ffc107;
color: #333;
}
.alarm-delete {
background: #dc3545;
color: white;
}
.alarm-edit:hover,
.alarm-delete:hover {
opacity: 0.9;
}
/* Modal Styles */
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 2000;
overflow-y: auto;
}
.modal-content {
background: white;
max-width: 500px;
margin: 50px auto;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
}
.modal-header {
padding: 1rem;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h2 {
margin: 0;
font-size: 1.5rem;
}
.close {
font-size: 1.5rem;
cursor: pointer;
color: #888;
}
.close:hover {
color: #333;
}
.modal-body {
padding: 1.5rem;
}
.form-group {
margin-bottom: 1.5rem;
text-align: left;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: #333;
}
.form-control {
width: 100%;
padding: 0.8rem;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 1rem;
}
.days-selector {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.day-checkbox {
display: flex;
align-items: center;
gap: 0.2rem;
padding: 0.3rem 0.6rem;
background: #f8f9fa;
border-radius: 3px;
cursor: pointer;
}
.day-checkbox:hover {
background: #e9ecef;
}
.volume-slider {
width: 100%;
margin-bottom: 0.5rem;
}
#volumeValue {
margin-left: 0.5rem;
font-weight: 500;
}
.form-actions {
display: flex;
gap: 1rem;
margin-top: 2rem;
}
.btn-save,
.btn-cancel {
flex: 1;
padding: 0.8rem;
border: none;
border-radius: 5px;
font-size: 1rem;
cursor: pointer;
transition: opacity 0.3s;
}
.btn-save {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-cancel {
background: #6c757d;
color: white;
}
.btn-save:hover,
.btn-cancel:hover {
opacity: 0.9;
}
</style>
<?php include '../includes/footer.php'; ?>
7. js/alarms.js (Alarm Management)
/**
* Alarm Management JavaScript
*/
// Show add alarm modal
function showAddAlarmModal() {
document.getElementById('modalTitle').textContent = 'Add Alarm';
document.getElementById('alarmId').value = '';
document.getElementById('alarmForm').reset();
document.getElementById('alarmTime').value = '08:00';
document.getElementById('alarmModal').style.display = 'block';
}
// Show edit alarm modal
function showEditAlarmModal(id) {
const alarm = alarms.find(a => a.id === id);
if (!alarm) return;
document.getElementById('modalTitle').textContent = 'Edit Alarm';
document.getElementById('alarmId').value = alarm.id;
document.getElementById('alarmTime').value = alarm.time;
document.getElementById('alarmLabel').value = alarm.label;
document.getElementById('alarmSound').value = alarm.sound;
document.getElementById('alarmVolume').value = alarm.volume;
document.getElementById('volumeValue').textContent = alarm.volume + '%';
document.getElementById('snoozeEnabled').checked = alarm.snoozeEnabled;
document.getElementById('snoozeDuration').value = alarm.snoozeDuration;
// Set days
document.querySelectorAll('input[name="days[]"]').forEach(cb => {
cb.checked = alarm.days.includes(parseInt(cb.value));
});
document.getElementById('alarmModal').style.display = 'block';
}
// Close alarm modal
function closeAlarmModal() {
document.getElementById('alarmModal').style.display = 'none';
}
// Save alarm
function saveAlarm(event) {
event.preventDefault();
const id = document.getElementById('alarmId').value;
const time = document.getElementById('alarmTime').value;
const label = document.getElementById('alarmLabel').value;
const sound = document.getElementById('alarmSound').value;
const volume = parseInt(document.getElementById('alarmVolume').value);
const snoozeEnabled = document.getElementById('snoozeEnabled').checked;
const snoozeDuration = parseInt(document.getElementById('snoozeDuration').value);
// Get selected days
const days = [];
document.querySelectorAll('input[name="days[]"]:checked').forEach(cb => {
days.push(parseInt(cb.value));
});
const alarmData = {
time,
label,
days,
sound,
volume,
snoozeEnabled,
snoozeDuration
};
if (id) {
// Update existing alarm
updateAlarm(id, alarmData);
} else {
// Add new alarm
addAlarm(alarmData);
}
closeAlarmModal();
updateAlarmsList();
}
// Update alarms list (override from clock.js)
function updateAlarmsList() {
const container = document.getElementById('alarmsList');
if (!container) return;
if (alarms.length === 0) {
container.innerHTML = '<p class="no-alarms">No alarms set. Click "Add New Alarm" to create one.</p>';
return;
}
// Sort alarms by time
const sorted = [...alarms].sort((a, b) => {
return a.time.localeCompare(b.time);
});
let html = '';
sorted.forEach(alarm => {
const [hours, minutes] = alarm.time.split(':');
const time12 = formatTime12(parseInt(hours), parseInt(minutes));
html += `
<div class="alarm-item">
<div class="alarm-time">${time12}</div>
<div class="alarm-details">
<div class="alarm-label">${escapeHtml(alarm.label)}</div>
<div class="alarm-schedule">${formatDays(alarm.days)}</div>
</div>
<div class="alarm-actions">
<button class="alarm-edit" onclick="showEditAlarmModal('${alarm.id}')">Edit</button>
<button class="alarm-delete" onclick="deleteAlarm('${alarm.id}')">Delete</button>
</div>
<label class="alarm-switch">
<input type="checkbox" ${alarm.isActive ? 'checked' : ''}
onchange="toggleAlarm('${alarm.id}')">
<span class="alarm-slider"></span>
</label>
</div>
`;
});
container.innerHTML = html;
}
// Update volume display
document.getElementById('alarmVolume')?.addEventListener('input', function(e) {
document.getElementById('volumeValue').textContent = e.target.value + '%';
});
// Toggle snooze fields
document.getElementById('snoozeEnabled')?.addEventListener('change', function(e) {
document.getElementById('snoozeGroup').style.display = e.target.checked ? 'block' : 'none';
});
// Initialize on page load
document.addEventListener('DOMContentLoaded', function() {
loadAlarmsFromStorage();
updateAlarmsList();
});
🚀 How to Use This Project Step by Step
Step 1: Setup
- Copy all files to your web server
- Ensure proper permissions for sound files
- Configure
.envif using database storage
Step 2: Database (Optional)
If using server-side storage:
mysql -u root -p < database/clock.sql
Step 3: Access the Clock
Navigate to /clock/index.php in your browser
Step 4: Set Up Alarms
- Click on "Alarms" tab
- Click "Add New Alarm"
- Set time, label, repeat days
- Choose sound and volume
- Save alarm
Step 5: Use Timer
- Go to Timer tab
- Set hours, minutes, seconds
- Click Start
- Timer will notify when complete
Step 6: Use Stopwatch
- Go to Stopwatch tab
- Click Start to begin timing
- Use Lap button to record splits
- Reset to start over
Step 7: Customize Settings
- Go to Settings tab
- Choose time format (12/24 hour)
- Select theme (Light/Dark/Auto)
- Set default sound and volume
- Configure snooze preferences
🔒 Security Features
Client-Side Security:
- ✅ Alarms stored in localStorage (no server needed)
- ✅ XSS protection with escapeHtml function
- ✅ Input validation for all forms
- ✅ Secure audio playback
Optional Server-Side:
- ✅ Database storage with prepared statements
- ✅ User authentication integration
- ✅ Rate limiting for API calls
- ✅ Encrypted sensitive data
🎯 Keyboard Shortcuts
| Key | Function |
|---|---|
| Space | Snooze active alarm |
| Escape | Dismiss active alarm |
| Ctrl+F | Toggle fullscreen |
| Ctrl+T | Toggle theme |
| Ctrl+1 | Go to Clock |
| Ctrl+2 | Go to Alarms |
| Ctrl+3 | Go to Timer |
| Ctrl+4 | Go to Stopwatch |
| Ctrl+S | Start/Stop timer |
🚀 Future Enhancements
- Advanced Features:
- Sunrise simulation alarm
- Smart wake (light sleep detection)
- Weather integration
- Calendar sync
- Sound Options:
- Custom sound upload
- Spotify/Apple Music integration
- Gradual volume increase
- Playlist support
- Visual Features:
- Custom clock faces
- Animated backgrounds
- Weather display
- News ticker
- Smart Features:
- Voice control
- IFTTT integration
- Smart home integration
- Bedtime reminders
- Analytics:
- Sleep tracking
- Wake-up patterns
- Alarm effectiveness
- Usage statistics
📝 Conclusion
This Digital Clock with Alarm System provides a complete time management solution with beautiful analog and digital displays, comprehensive alarm management, timer functionality, and stopwatch features. The system is built with pure HTML, CSS, and JavaScript for the frontend, making it lightweight and fast. With support for multiple alarms, customizable sounds, and various themes, it's perfect for any website needing time management tools or as a standalone productivity application.