How to Fix It Timer Displaying Delay in JavaScript

If you’ve built a JavaScript countdown timer and noticed it doesn’t align with real time, you’re not alone. In this article, we’ll diagnose why delays occur in timers like this JSFiddle example and implement a fix. We’ll also enhance the timer with practical features like pause/resume, custom durations, and a progress bar.

Why the Delay Happens

The original code uses setInterval to decrement a sec variable every second. However, setInterval isn’t perfectly precise—it schedules tasks approximately every 1000ms. Browser operations, background tabs, or heavy scripts can delay these intervals. Over time, these small delays compound, causing the timer to drift significantly.

For example:

  • A 10-minute timer might show 01:10 remaining after 10 real minutes.
  • A 30-minute timer could lag by 5+ minutes.

The root cause: The timer relies on the number of interval cycles rather than actual elapsed time.

Use System Time for Accuracy

Instead of decrementing sec blindly, calculate the remaining time using the system clock. Here’s the revised code:

let initialSec = 600; // 10 minutes in seconds
let countDiv = document.getElementById("timer");
let startTime = Date.now();
let countDown = setInterval(updateTimer, 1000);

function updateTimer() {
  const currentTime = Date.now();
  const elapsed = Math.floor((currentTime - startTime) / 1000);
  const remaining = initialSec - elapsed;

  const min = String(Math.floor(remaining / 60)).padStart(2, '0');
  const remSec = String(remaining % 60).padStart(2, '0');

  countDiv.innerHTML = `${min}:${remSec}`;

  if (remaining <= 0) {
    clearInterval(countDown);
    countDiv.innerHTML = 'Countdown Done!';
  }
}

Key changes:

  1. Track startTime using Date.now() for accuracy.
  2. Calculate elapsed time dynamically.
  3. Use padStart to format minutes and seconds.

Enhancing the Timer with Features

Pause/Resume Functionality

Add buttons to pause and resume the timer:

<button id="pauseBtn">Pause</button>
<button id="resumeBtn" style="display: none;">Resume</button>

Run HTML

let isPaused = false;
let remainingOnPause;

document.getElementById('pauseBtn').addEventListener('click', pauseTimer);
document.getElementById('resumeBtn').addEventListener('click', resumeTimer);

function pauseTimer() {
  clearInterval(countDown);
  isPaused = true;
  remainingOnPause = initialSec - Math.floor((Date.now() - startTime) / 1000);
  document.getElementById('pauseBtn').style.display = 'none';
  document.getElementById('resumeBtn').style.display = 'inline';
}

function resumeTimer() {
  isPaused = false;
  initialSec = remainingOnPause;
  startTime = Date.now();
  countDown = setInterval(updateTimer, 1000);
  document.getElementById('pauseBtn').style.display = 'inline';
  document.getElementById('resumeBtn').style.display = 'none';
}

Custom Duration Input

Let users set their own timer duration:

<input type="number" id="minutesInput" placeholder="Minutes" value="10">
<button id="startBtn">Start</button>

Run HTML

document.getElementById('startBtn').addEventListener('click', () => {
  const minutes = parseInt(document.getElementById('minutesInput').value);
  if (!isNaN(minutes)) {
    initialSec = minutes * 60;
    startTime = Date.now();
    if (countDown) clearInterval(countDown);
    countDown = setInterval(updateTimer, 1000);
  }
});

Progress Bar

Visualize time progression with a CSS-based bar:

<div class="progress-container">
  <div id="progressBar" class="progress-bar"></div>
</div>

Run HTML

.progress-container {
  width: 100%;
  height: 4px;
  background: #ddd;
  margin-top: 10px;
}

.progress-bar {
  height: 100%;
  background: crimson;
  width: 0%;
  transition: width 0.3s ease;
}

Update the progress in updateTimer():

const progress = (remaining / initialSec) * 100;
document.getElementById('progressBar').style.width = `${progress}%`;

Final Thoughts

Timers relying solely on setInterval are prone to drift. By anchoring calculations to the system clock (Date.now()), we ensure accuracy regardless of interval delays. Adding features like pause/resume, custom inputs, and visual feedback makes the timer more robust and user-friendly.

Related blog posts