Build a Time Tracker Chrome Extension: Monitor Your Browsing Habits
In today’s digital age, understanding how we spend our time online has become crucial for maintaining productivity and digital wellness. Whether you’re a freelancer tracking billable hours, a student monitoring study time, or a professional trying to minimize distractions, a well-designed time tracker Chrome extension can provide invaluable insights into your browsing habits. This comprehensive guide will walk you through building a fully functional time tracking extension using Chrome’s Manifest V3 standards, giving you the tools to monitor website usage, analyze productivity patterns, and take control of your digital time.
The demand for browsing time monitors has surged dramatically as more people work remotely and rely heavily on web-based applications. Understanding where your time goes throughout the workday can reveal surprising patterns and help you make informed decisions about how to improve your productivity. By building your own time tracker extension, you gain complete control over your data while learning valuable skills in Chrome extension development.
Understanding the Need for Website Time Monitoring
The modern browser has become the primary workspace for millions of professionals worldwide. We use it for communication, research, document creation, project management, and countless other tasks. However, this convenience comes with a downside: it’s easy to lose track of time while browsing. Hours can vanish checking social media, reading news, or falling down YouTube rabbit holes without any awareness of how much time these activities consume.
A website time monitor extension solves this problem by automatically tracking how long you spend on each website. Unlike manual time tracking methods that require you to remember to start and stop timers, a passive monitoring extension works in the background, capturing data without interrupting your workflow. This automatic tracking provides accurate, unbiased data about your actual browsing behavior.
The benefits of monitoring your browsing habits extend beyond simple curiosity. By analyzing your time data, you can identify time-wasting patterns, set realistic goals for reducing distracting browsing, and hold yourself accountable for how you spend your work hours. For businesses, understanding employee browsing patterns can inform policies around acceptable use and help identify training needs. Parents can use these tools to monitor and guide their children’s online activities responsibly.
Building a time tracker extension also represents an excellent learning opportunity for developers. It involves several key Chrome extension concepts including the tabs API for monitoring active tab changes, the storage API for persisting time data, background service workers for continuous monitoring, and message passing for communication between extension components. Mastering these concepts will prepare you for building more complex extensions in the future.
Project Architecture and Features
Before diving into code, let’s define the architecture and features for our time tracker extension. A robust website time monitor should capture detailed information about browsing sessions while remaining lightweight and privacy-focused.
Core Features
Our time tracker extension will include the following essential features:
- Automatic Tab Tracking: Monitor which tabs are active and track time spent on each website
- Domain-Level Aggregation: Group time data by domain to provide meaningful insights
- Daily Summary Dashboard: Display today’s browsing time across all websites
- Top Sites List: Show the websites where you spend the most time
- Visual Reports: Provide easy-to-understand visualizations of your browsing patterns
- Privacy-First Storage: Keep all data local on the user’s device
- Idle Detection: Pause tracking when you’re away from your computer
This feature set balances functionality with simplicity, making it achievable within a comprehensive tutorial while still providing real value to users.
Technical Architecture
The extension will use a clean architecture with separate responsibilities for each component:
- Popup Interface: Displays time statistics and quick controls
- Background Service Worker: Manages tracking logic and stores data
- Content Script: Captures page visibility changes
- Storage Layer: Uses chrome.storage.local for persistent data
This separation of concerns ensures the extension remains maintainable and easy to extend with additional features later.
Setting Up the Project Structure
Every Chrome extension begins with the manifest file, which defines the extension’s capabilities and permissions. Let’s create the foundation of our time tracker extension.
Creating manifest.json
The manifest.json file tells Chrome about our extension’s configuration, permissions, and components:
{
"manifest_version": 3,
"name": "BrowseTime - Website Time Monitor",
"version": "1.0.0",
"description": "Monitor your browsing habits and track time spent on websites",
"permissions": [
"tabs",
"storage",
"alarms",
"idle"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}
This manifest requests the minimum permissions necessary for time tracking: tabs for accessing URL information, storage for persisting data, alarms for scheduling, and idle for detecting when the user is away.
Project File Structure
Create a folder structure like this:
time-tracker-extension/
├── manifest.json
├── background.js
├── popup.html
├── popup.js
├── popup.css
├── icons/
│ ├── icon16.png
│ ├── icon48.png
│ └── icon128.png
└── _locales/
└── en/
└── messages.json
Organizing files systematically makes the extension easier to maintain and debug. The icons folder contains the extension icons at various sizes, while the locales folder enables internationalization if you want to support multiple languages later.
Implementing the Background Service Worker
The service worker serves as the brain of our extension, handling time tracking logic and data storage. It runs continuously in the background, monitoring tab changes and maintaining accurate time records.
Core Tracking Logic
Create background.js with the following implementation:
// Background service worker for time tracking extension
// Store for active tab tracking
let activeTabInfo = {
tabId: null,
domain: null,
startTime: null
};
// Initialize extension
chrome.runtime.onInstalled.addListener(() => {
// Initialize storage with empty daily data
chrome.storage.local.set({
timeData: {},
dailyStats: {}
});
// Set up idle detection
chrome.idle.setDetectionInterval(60); // Check every minute
});
// Listen for tab updates
chrome.tabs.onActivated.addListener(handleTabChange);
chrome.tabs.onUpdated.addListener(handleTabUpdate);
// Handle tab activation changes
async function handleTabChange(activeInfo) {
await saveCurrentTabTime();
await startTrackingTab(activeInfo.tabId);
}
// Handle tab URL updates
async function handleTabUpdate(tabId, changeInfo, tab) {
if (changeInfo.url && activeTabInfo.tabId === tabId) {
await saveCurrentTabTime();
await startTrackingTab(tabId);
}
}
// Save time for the currently tracked tab
async function saveCurrentTabTime() {
if (!activeTabInfo.tabId || !activeTabInfo.domain || !activeTabInfo.startTime) {
return;
}
const endTime = Date.now();
const duration = endTime - activeTabInfo.startTime;
// Get existing data
const result = await chrome.storage.local.get('timeData');
const timeData = result.timeData || {};
// Update domain time
if (!timeData[activeTabInfo.domain]) {
timeData[activeTabInfo.domain] = {
totalTime: 0,
visits: 0,
today: 0
};
}
timeData[activeTabInfo.domain].totalTime += duration;
timeData[activeTabInfo.domain].visits += 1;
timeData[activeTabInfo.domain].today += duration;
// Save updated data
await chrome.storage.local.set({ timeData });
// Reset active tracking
activeTabInfo = {
tabId: null,
domain: null,
startTime: null
};
}
// Start tracking a new tab
async function startTrackingTab(tabId) {
try {
const tab = await chrome.tabs.get(tabId);
if (!tab.url || tab.url.startsWith('chrome://') ||
tab.url.startsWith('chrome-extension://')) {
return;
}
const url = new URL(tab.url);
const domain = url.hostname;
activeTabInfo = {
tabId: tabId,
domain: domain,
startTime: Date.now()
};
} catch (error) {
console.error('Error starting tab tracking:', error);
}
}
// Handle idle state
chrome.idle.onStateChanged.addListener(async (state) => {
if (state === 'idle') {
// User is away, save current tracking
await saveCurrentTabTime();
} else if (state === 'active') {
// User returned, resume tracking
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
if (tabs[0]) {
await startTrackingTab(tabs[0].id);
}
}
});
// Reset daily stats at midnight
chrome.alarms.create('resetDaily', {
delayInMinutes: getMinutesUntilMidnight()
});
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'resetDaily') {
resetDailyStats();
chrome.alarms.create('resetDaily', {
delayInMinutes: 1440 // 24 hours
});
}
});
async function resetDailyStats() {
const result = await chrome.storage.local.get('timeData');
const timeData = result.timeData || {};
for (const domain in timeData) {
timeData[domain].today = 0;
}
await chrome.storage.local.set({ timeData });
}
function getMinutesUntilMidnight() {
const now = new Date();
const midnight = new Date(now);
midnight.setHours(24, 0, 0, 0);
return (midnight - now) / 60000;
}
This background script handles the core tracking functionality. It monitors tab changes, calculates time spent on each domain, and stores the data persistently. The idle detection feature pauses tracking when you’re away, ensuring accurate time measurements.
Building the Popup Interface
The popup provides users with quick access to their time tracking data. It should be clean, informative, and responsive.
HTML Structure
Create popup.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BrowseTime</title>
<link rel="stylesheet" href="popup.css">
</head>
<body>
<div class="container">
<header>
<h1>BrowseTime</h1>
<p class="subtitle">Monitor Your Browsing</p>
</header>
<section class="summary">
<div class="stat-card">
<span class="stat-label">Today's Total</span>
<span class="stat-value" id="totalTime">0h 0m</span>
</div>
<div class="stat-card">
<span class="stat-label">Sites Visited</span>
<span class="stat-value" id="sitesVisited">0</span>
</div>
</section>
<section class="top-sites">
<h2>Top Sites Today</h2>
<ul id="topSitesList">
<li class="loading">Loading...</li>
</ul>
</section>
<section class="actions">
<button id="viewDashboard" class="btn btn-primary">View Dashboard</button>
<button id="clearData" class="btn btn-secondary">Clear Data</button>
</section>
</div>
<script src="popup.js"></script>
</body>
</html>
Styling the Popup
Create popup.css to make the interface visually appealing:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
width: 320px;
background: #f5f5f5;
}
.container {
padding: 16px;
}
header {
text-align: center;
margin-bottom: 16px;
}
h1 {
font-size: 20px;
color: #1a73e8;
margin-bottom: 4px;
}
.subtitle {
font-size: 12px;
color: #666;
}
.summary {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-bottom: 16px;
}
.stat-card {
background: white;
padding: 12px;
border-radius: 8px;
text-align: center;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.stat-label {
display: block;
font-size: 11px;
color: #666;
margin-bottom: 4px;
text-transform: uppercase;
}
.stat-value {
display: block;
font-size: 18px;
font-weight: 600;
color: #1a73e8;
}
.top-sites {
background: white;
border-radius: 8px;
padding: 12px;
margin-bottom: 16px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.top-sites h2 {
font-size: 14px;
color: #333;
margin-bottom: 12px;
}
#topSitesList {
list-style: none;
}
#topSitesList li {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #eee;
font-size: 13px;
}
#topSitesList li:last-child {
border-bottom: none;
}
.site-domain {
color: #333;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 180px;
}
.site-time {
color: #666;
font-weight: 500;
}
.loading {
color: #999;
font-style: italic;
}
.actions {
display: flex;
gap: 8px;
}
.btn {
flex: 1;
padding: 10px;
border: none;
border-radius: 6px;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
}
.btn-primary {
background: #1a73e8;
color: white;
}
.btn-primary:hover {
background: #1557b0;
}
.btn-secondary {
background: #e8eaed;
color: #333;
}
.btn-secondary:hover {
background: #d3d3d3;
}
Popup JavaScript Logic
Create popup.js to handle user interactions:
document.addEventListener('DOMContentLoaded', async () => {
await loadTimeData();
setupEventListeners();
});
async function loadTimeData() {
try {
const result = await chrome.storage.local.get('timeData');
const timeData = result.timeData || {};
updateSummary(timeData);
updateTopSites(timeData);
} catch (error) {
console.error('Error loading time data:', error);
}
}
function updateSummary(timeData) {
let totalToday = 0;
let totalVisits = 0;
for (const domain in timeData) {
totalToday += timeData[domain].today || 0;
totalVisits += timeData[domain].visits || 0;
}
document.getElementById('totalTime').textContent = formatTime(totalToday);
document.getElementById('sitesVisited').textContent = totalVisits.toString();
}
function updateTopSites(timeData) {
const sitesList = document.getElementById('topSitesList');
// Convert to array and sort by time
const sites = Object.entries(timeData)
.map(([domain, data]) => ({
domain,
time: data.today || 0,
visits: data.visits || 0
}))
.filter(site => site.time > 0)
.sort((a, b) => b.time - a.time)
.slice(0, 5);
if (sites.length === 0) {
sitesList.innerHTML = '<li class="loading">No data yet</li>';
return;
}
sitesList.innerHTML = sites.map(site => `
<li>
<span class="site-domain">${site.domain}</span>
<span class="site-time">${formatTime(site.time)}</span>
</li>
`).join('');
}
function formatTime(milliseconds) {
const seconds = Math.floor(milliseconds / 1000);
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
if (hours > 0) {
return `${hours}h ${minutes}m`;
}
return `${minutes}m`;
}
function setupEventListeners() {
document.getElementById('viewDashboard').addEventListener('click', () => {
// Open dashboard in new tab
chrome.tabs.create({ url: 'dashboard.html' });
});
document.getElementById('clearData').addEventListener('click', async () => {
if (confirm('Are you sure you want to clear all tracking data?')) {
await chrome.storage.local.set({ timeData: {}, dailyStats: {} });
await loadTimeData();
}
});
}
Testing Your Extension
Before publishing, thoroughly test your extension to ensure it works correctly. Load it in Chrome’s developer mode and verify each feature functions as expected.
Loading the Extension
- Open Chrome and navigate to chrome://extensions/
- Enable “Developer mode” in the top right corner
- Click “Load unpacked” and select your extension folder
- The extension icon should appear in your toolbar
Testing Checklist
- Open various websites and verify time is being tracked
- Switch between tabs and confirm tracking continues accurately
- Let the extension sit idle and verify idle detection works
- Check the popup displays accurate time data
- Verify data persists after closing and reopening Chrome
Enhancing and Extending Your Extension
Now that you have a working time tracker, consider adding these advanced features to make it even more valuable:
Daily and Weekly Reports
Create a dashboard page that visualizes time data over longer periods. Use Chart.js or similar libraries to display bar charts, line graphs, and pie charts showing time distribution across categories.
Category-Based Tracking
Implement automatic categorization of websites (social media, news, productivity tools, entertainment). Use domain matching rules to group sites and provide higher-level insights into your browsing habits.
Productivity Scores
Develop an algorithm that calculates productivity scores based on time spent on different categories. Award points for productive sites and deduct for distracting ones.
Data Export
Allow users to export their data as CSV or JSON files for analysis in spreadsheet applications or to back up their information.
Publishing Your Extension
Once you’re satisfied with your extension, you can publish it to the Chrome Web Store:
- Create a developer account at the Chrome Web Store
- Package your extension as a ZIP file
- Upload and fill in the store listing details
- Submit for review
Ensure your extension’s privacy policy clearly explains what data you collect and how you use it. The Chrome Web Store has strict policies around user data handling.
Conclusion
Building a time tracker Chrome extension is an excellent project that teaches valuable skills while creating a genuinely useful tool. You’ve learned how to work with Chrome’s APIs for tab tracking, implement background service workers, create popup interfaces, and store data persistently.
The extension you built follows privacy-first principles by keeping all data local on the user’s device. This approach not only respects user privacy but also eliminates the complexity of managing cloud infrastructure.
As you continue developing Chrome extensions, you’ll find that the concepts learned here apply broadly. The patterns for tracking state, communicating between components, and managing user data are foundational skills for any extension developer.
Consider expanding your extension with the enhancement ideas mentioned above, or use your new knowledge to build entirely different types of extensions. The Chrome extension ecosystem offers endless possibilities for creating tools that improve productivity, accessibility, and user experience.