Chrome Extension Timer Extension — Developer Guide

6 min read

Build a Countdown Timer Extension

What You’ll Build

Manifest


Step 1: Manifest Configuration

Define required permissions in manifest.json:

{
  "name": "Countdown Timer",
  "version": "1.0",
  "permissions": [
    "alarms",
    "notifications", 
    "storage"
  ],
  "action": {
    "default_popup": "popup.html"
  },
  "background": {
    "service_worker": "background.js"
  }
}

See permissions/alarms.md and permissions/notifications.md.


Step 2: Popup UI with Timer Input

Create the timer interface with input fields and controls:

<!-- popup.html -->
<div class="timer-app">
  <div class="timer-input">
    <input type="number" id="hours" min="0" max="23" placeholder="00">
    <span>:</span>
    <input type="number" id="minutes" min="0" max="59" placeholder="00">
    <span>:</span>
    <input type="number" id="seconds" min="0" max="59" placeholder="00">
  </div>
  <div class="presets">
    <button data-time="300">5 min</button>
    <button data-time="900">15 min</button>
    <button data-time="1800">30 min</button>
  </div>
  <div class="controls">
    <button id="start">Start</button>
    <button id="pause">Pause</button>
    <button id="reset">Reset</button>
  </div>
  <div id="timer-list" class="timer-list"></div>
</div>

Step 3: Timer State with chrome.storage.session

Use session storage for fast, ephemeral timer state:

// background/timer-state.js
const DEFAULT_TIMER = {
  id: null,
  endTime: null,
  isRunning: false,
  totalSeconds: 0,
  remainingSeconds: 0
};

async function saveTimerState(timer) {
  await chrome.storage.session.set({ activeTimer: timer });
}

async function loadTimerState() {
  const data = await chrome.storage.session.get(['activeTimer']);
  return data.activeTimer || DEFAULT_TIMER;
}

See storage-quickstart.md.


Step 4: Background Alarms

Create one alarm per timer with chrome.alarms API:

// background/alarm-manager.js
const ALARM_NAME = 'countdown-timer';

function createTimerAlarm(delayInMinutes) {
  chrome.alarms.create(ALARM_NAME, {
    delayInMinutes: Math.max(1, delayInMinutes), // 1-minute minimum
    periodInMinutes: 1 // Repeat every minute for updates
  });
}

chrome.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === ALARM_NAME) {
    handleTimerTick();
  }
});

Note: Alarms have a 1-minute minimum. For precise countdown display, use storage timestamps and calculate remaining time in the popup.

See api-reference/alarms-api.md.


Step 5: Badge Countdown

Display remaining time on the extension badge:

// background/badge-manager.js
async function updateBadge() {
  const timer = await loadTimerState();
  
  if (timer.isRunning && timer.remainingSeconds > 0) {
    const minutes = Math.ceil(timer.remainingSeconds / 60);
    await chrome.action.setBadgeText({ text: String(minutes) });
    await chrome.action.setBadgeBackgroundColor({ 
      color: '#4CAF50' 
    });
  } else {
    await chrome.action.setBadgeText({ text: '' });
  }
}

See patterns/badge-action-ui.md.


Step 6: Notification on Completion

Notify users when timers complete with sound:

// background/notifications.js
async function notifyTimerComplete(timerName) {
  await chrome.notifications.create({
    type: 'basic',
    iconUrl: 'icons/icon-48.png',
    title: 'Timer Complete!',
    message: `${timerName || 'Your timer'} has finished.`,
    priority: 2
  });
  
  // Play sound via audio element in popup context
  chrome.runtime.sendMessage({ action: 'playSound' });
}

See api-reference/notifications-api.md.


Step 7: Multiple Timer Management

Support multiple simultaneous timers with list view:

// background/multi-timer.js
async function addTimer(duration, name) {
  const timers = await getAllTimers();
  const id = Date.now().toString();
  
  const newTimer = {
    id,
    name,
    endTime: Date.now() + duration * 1000,
    duration,
    isRunning: true
  };
  
  timers.push(newTimer);
  await chrome.storage.local.set({ timers });
  createTimerAlarm(duration / 60, id);
  
  return newTimer;
}

async function getAllTimers() {
  const data = await chrome.storage.local.get(['timers']);
  return data.timers || [];
}

Step 8: Service Worker Restart Recovery

Reconstruct timer state from storage and alarms on restart:

// background/startup.js
chrome.runtime.onStartup.addListener(async () => {
  const timers = await getAllTimers();
  
  for (const timer of timers) {
    if (timer.isRunning && timer.endTime > Date.now()) {
      // Recalculate remaining time
      timer.remainingSeconds = Math.floor(
        (timer.endTime - Date.now()) / 1000
      );
      await chrome.storage.session.set({ activeTimer: timer });
      
      // Recreate alarm with remaining time
      const remainingMinutes = timer.remainingSeconds / 60;
      if (remainingMinutes >= 1) {
        createTimerAlarm(remainingMinutes, timer.id);
      }
    }
  }
});

Summary

Your countdown timer extension now includes:

This demonstrates chrome.alarms for background timing, chrome.storage for state persistence, and badge management for visual feedback. -e —

Part of the Chrome Extension Guide by theluckystrike. Built at zovo.one.

No previous article
No next article