UTILITY
STOP Watch
Precise stopwatch with lap times and countdown timer.
00:00:00.000
:
:
00:05:00
Developer Reference
Core Algorithm & Standalone Script
Standalone, zero-dependency JavaScript implementation powering this tool. Free to inspect, copy, and build upon.
// Tab switching
document.querySelectorAll('#mode-tabs .tab').forEach(t => {
t.addEventListener('click', () => {
document.querySelectorAll('#mode-tabs .tab').forEach(x => x.classList.remove('active'));
t.classList.add('active');
document.getElementById('sw-panel').classList.toggle('hidden', t.dataset.mode !== 'stopwatch');
document.getElementById('timer-panel').classList.toggle('hidden', t.dataset.mode !== 'timer');
});
});
// === STOPWATCH ===
let swRunning = false, swStart = 0, swElapsed = 0, swInterval, laps = [], lapStart = 0;
const pad = (n, d = 2) => String(n).padStart(d, '0');
function fmtTime(ms) {
const h = Math.floor(ms / 3600000);
const m = Math.floor((ms % 3600000) / 60000);
const s = Math.floor((ms % 60000) / 1000);
const ml = ms % 1000;
return `${pad(h)}:${pad(m)}:${pad(s)}<span class="ms">.${pad(ml, 3)}</span>`;
}
function updateSW() {
const total = swElapsed + (Date.now() - swStart);
document.getElementById('sw-display').innerHTML = fmtTime(total);
}
document.getElementById('sw-start').addEventListener('click', function() {
if (!swRunning) {
swRunning = true;
swStart = Date.now();
if (laps.length === 0) lapStart = swStart;
swInterval = setInterval(updateSW, 10);
this.textContent = 'Stop';
this.classList.replace('btn-primary', 'btn-secondary');
document.getElementById('sw-lap').disabled = false;
document.getElementById('sw-reset').disabled = false;
} else {
swRunning = false;
swElapsed += Date.now() - swStart;
clearInterval(swInterval);
this.textContent = 'Start';
this.classList.replace('btn-secondary', 'btn-primary');
}
});
document.getElementById('sw-lap').addEventListener('click', () => {
if (!swRunning) return;
const now = Date.now();
const total = swElapsed + (now - swStart);
const lapTime = total - (laps.length > 0 ? laps.reduce((a, b) => a + b, 0) : 0);
laps.push(lapTime);
const container = document.getElementById('sw-laps');
container.innerHTML = laps.map((l, i) =>
`<div class="lap-row"><span>Lap ${i + 1}</span><span>${pad(Math.floor(l / 60000))}:${pad(Math.floor((l % 60000) / 1000))}.${pad(l % 1000, 3)}</span></div>`
).reverse().join('');
});
document.getElementById('sw-reset').addEventListener('click', () => {
swRunning = false; swElapsed = 0; laps = [];
clearInterval(swInterval);
document.getElementById('sw-display').innerHTML = fmtTime(0);
document.getElementById('sw-start').textContent = 'Start';
document.getElementById('sw-start').classList.replace('btn-secondary', 'btn-primary');
document.getElementById('sw-lap').disabled = true;
document.getElementById('sw-reset').disabled = true;
document.getElementById('sw-laps').innerHTML = '';
});
// === TIMER ===
let tRunning = false, tRemaining = 0, tInterval;
function updateTimerDisplay() {
const h = Math.floor(tRemaining / 3600);
const m = Math.floor((tRemaining % 3600) / 60);
const s = tRemaining % 60;
document.getElementById('t-display').textContent = `${pad(h)}:${pad(m)}:${pad(s)}`;
}
function getTimerSeconds() {
return (parseInt(document.getElementById('t-h').value) || 0) * 3600 +
(parseInt(document.getElementById('t-m').value) || 0) * 60 +
(parseInt(document.getElementById('t-s').value) || 0);
}
['t-h', 't-m', 't-s'].forEach(id => document.getElementById(id).addEventListener('input', () => {
if (!tRunning) { tRemaining = getTimerSeconds(); updateTimerDisplay(); }
}));
tRemaining = getTimerSeconds();
document.getElementById('t-start').addEventListener('click', function() {
if (!tRunning) {
if (tRemaining <= 0) tRemaining = getTimerSeconds();
if (tRemaining <= 0) return;
tRunning = true;
this.textContent = 'Pause';
document.getElementById('t-display').classList.remove('flash');
tInterval = setInterval(() => {
tRemaining--;
updateTimerDisplay();
if (tRemaining <= 0) {
clearInterval(tInterval);
tRunning = false;
this.textContent = 'Start';
document.getElementById('t-display').classList.add('flash');
document.getElementById('t-display').style.color = 'var(--accent)';
}
}, 1000);
} else {
tRunning = false;
clearInterval(tInterval);
this.textContent = 'Start';
}
});
document.getElementById('t-reset').addEventListener('click', () => {
tRunning = false;
clearInterval(tInterval);
tRemaining = getTimerSeconds();
updateTimerDisplay();
document.getElementById('t-start').textContent = 'Start';
document.getElementById('t-display').classList.remove('flash');
document.getElementById('t-display').style.color = '';
});
// Keyboard
document.addEventListener('keydown', e => {
if (e.code === 'Space') { e.preventDefault(); document.getElementById('sw-start').click(); }
});