はじめに
科学的な時間管理術として広く知られるポモドーロテクニックを実践するためのWebタイマーアプリを開発しました。
ポモドーロテクニックとは:25分間の集中作業→5分休憩を繰り返し、4セット完了後に15-30分の長い休憩を取る時間管理術です。集中力の向上と疲労軽減に効果的な科学的手法として多くのエンジニアに活用されています。
ブラウザ最小化時も正確に動作する高精度タイマーと、作業フローを中断しない自動切り替え機能を搭載しています。
本記事について
今回のポモドーロタイマー開発およびこの記事執筆は、Claude Code(Anthropic社のAI)との協業で進めました。
AI時代の新しい開発スタイルの実践例として参考になれば幸いです。
この記事で分かること
- Page Visibility APIを使った高精度タイマーの実装方法
- ブラウザのバックグラウンド動作に対応したJavaScript設計
- ポモドーロテクニック特有の自動切り替え機能の実装
- ローカルストレージを使った日次データ管理
- レスポンシブ対応のタイマーUI設計
この記事の対象者
- フロントエンド開発初心者〜中級者
- タイマーアプリ開発に興味があるエンジニア
- Page Visibility API の実用例を知りたい方
- AI協業での開発手法に興味がある方
動作環境・使用技術
- 開発環境: Windows 11
- 言語: HTML, CSS, JavaScript
- 開発AI: Claude Code
- API: Page Visibility API, HTML5 Audio API
- ストレージ: Local Storage
自己紹介
ホネグミ代表、応用情報技術者の資格を持つエンジニア×マーケターです。
これまでIT系の会社役員を4年、独立して4年目になります。
クライアントワークでは「こうしたい」を技術で形にすることを専門としていますが、最近は思想駆動型サービス開発の第一人者として、AIを活用した様々なサービス開発を続けています。
完成品・デモ
主な機能:
- 25分作業・5分休憩・長休憩(15/20/25/30分)タイマー
- ブラウザ最小化対応の高精度計測
- タイマー完了時の自動切り替え機能
- 5種類のアラーム音と音量調整
- 本日の完了セット数管理
技術構成
アーキテクチャ概要
フロントエンド(HTML/CSS/JavaScript)
├── 高精度タイマー(Page Visibility API対応)
├── 音響システム(HTML5 Audio API)
├── 状態管理(Local Storage)
└── レスポンシブUI(CSS Grid/Flexbox)
使用技術スタック
- HTML5: セマンティックマークアップ、Audio要素
- CSS3: Grid/Flexbox、レスポンシブデザイン、CSS変数
- JavaScript ES6+: モジュール設計、非同期処理
- Page Visibility API: バックグラウンド動作対応
- Local Storage: クライアントサイドデータ管理
実装手順
1. 高精度タイマーの実装
従来のsetIntervalベースのタイマーは、ブラウザがバックグラウンドに移行すると動作が不安定になります。この問題を解決するため、実際の経過時間を基準とした高精度タイマーを実装しました。
// 高精度タイマー用変数
let startTimestamp = null;
let totalDuration = null;
let pausedTime = 0;
function startTimer() {
if (isRunning) return;
isRunning = true;
// 実際の開始時刻を記録(一時停止分を考慮)
startTimestamp = Date.now() - pausedTime * 1000;
totalDuration = currentTime + pausedTime;
timerInterval = setInterval(() => {
// 実際の経過時間を基に残り時間を算出
const now = Date.now();
const actualElapsed = Math.floor((now - startTimestamp) / 1000);
const expectedRemaining = totalDuration - actualElapsed;
if (expectedRemaining <= 0) {
currentTime = 0;
completeTimer();
} else {
currentTime = expectedRemaining;
updateDisplay();
updateTabTitle();
}
}, 100); // 高頻度更新で精度向上
}
2. Page Visibility API対応
ブラウザのタブ切り替えやアプリ最小化を検出し、フォアグラウンド復帰時に正確な時間を再計算します。
function setupVisibilityAPI() {
document.addEventListener('visibilitychange', function() {
if (isRunning) {
if (document.hidden) {
// バックグラウンドに移行
console.log('タイマーがバックグラウンドに移行');
} else {
// フォアグラウンドに復帰
console.log('タイマーがフォアグラウンドに復帰');
if (startTimestamp) {
// 実際の経過時間を再計算
const now = Date.now();
const actualElapsed = Math.floor((now - startTimestamp) / 1000);
const expectedRemaining = totalDuration - actualElapsed;
if (expectedRemaining <= 0) {
currentTime = 0;
completeTimer();
} else {
currentTime = expectedRemaining;
updateDisplay();
updateTabTitle();
}
}
}
}
});
}
3. 自動切り替え機能の実装
ポモドーロテクニックの作業フローを中断しないため、タイマー完了時に自動的に次のフェーズに移行する機能を実装しました。
function completeTimer() {
isRunning = false;
clearInterval(timerInterval);
// 高精度タイマー変数をリセット
startTimestamp = null;
totalDuration = null;
pausedTime = 0;
// セット数のカウント(作業時間完了時のみ)
if (currentMode === 'work') {
sessionCount++;
updateSessionDisplay();
}
// アラーム再生
playAlarm();
// 完了状態表示
timerStatus.textContent = '完了!';
document.title = '完了! - ポモドーロタイマー';
// 次のタイマーに自動切り替え
setTimeout(() => {
if (currentMode === 'work') {
// 作業完了後は5分休憩に切り替え
selectTime(5, 'break');
} else if (currentMode === 'break' || currentMode === 'longbreak') {
// 休憩完了後は25分作業に切り替え
selectTime(25, 'work');
}
}, 1000); // 1秒後に切り替え
saveData();
}
4. ローカルストレージによるデータ管理
セット数カウントと設定情報をブラウザローカルに保存し、日付変更時に自動リセットする機能を実装しました。
function saveData() {
const data = {
sessionCount: sessionCount,
volume: volumeSlider.value,
sound: soundSelect.value,
lastDate: new Date().toDateString()
};
localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
}
function loadData() {
const data = localStorage.getItem(STORAGE_KEY);
if (data) {
try {
const parsed = JSON.parse(data);
const today = new Date().toDateString();
// 日付が変わっていたらセッション数をリセット
if (parsed.lastDate === today) {
sessionCount = parsed.sessionCount || 0;
} else {
sessionCount = 0;
}
// 設定の復元
if (parsed.volume !== undefined) {
volumeSlider.value = parsed.volume;
updateSliderProgress();
}
if (parsed.sound) {
soundSelect.value = parsed.sound;
}
} catch (e) {
console.log('データ読み込みエラー:', e);
}
}
}
工夫したポイント・苦労した点
1. タイマー精度の課題と解決
最も苦労したのは、ブラウザのバックグラウンド動作での精度維持でした。通常のsetIntervalは、ブラウザがバックグラウンドに移行すると動作頻度が大幅に低下し、正確な時間計測ができません。
解決策として、Date.now()を基準とした実時間計算を採用しました。タイマー開始時刻を記録し、毎回の更新で実際の経過時間を計算することで、ブラウザの状態に関係なく正確な残り時間を表示できるようになりました。
2. UXを重視した自動切り替え設計
従来のタイマーアプリでは、作業完了後にユーザーが手動で休憩タイマーを開始する必要があり、この操作で集中が途切れる問題がありました。
自動切り替え機能により、ユーザーは開始操作のみで継続的なポモドーロセッションを実行できるようになりました。1秒の遅延を設けることで、完了状態を認識してから次のフェーズに移行する自然な流れを実現しています。
3. 音響機能の最適化
作業環境に配慮した音響設計として、5種類のアラーム音と細かな音量調整機能を実装しました。Material-UIスタイルの視覚的に分かりやすい音量スライダーにより、最適な作業環境を簡単に構築できます。
function playAlarm() {
const selectedSound = soundSelect.value;
const audio = document.getElementById('alarm-' + selectedSound);
if (audio) {
audio.volume = volumeSlider.value / 100;
audio.currentTime = 0;
audio.play().catch(e => console.log('音声再生に失敗:', e));
// 現在再生中の音声を記録
currentlyPlayingAudio = audio;
// 停止ボタンを表示
stopAlarmBtn.style.display = 'inline-flex';
}
}
まとめ
Page Visibility APIと実時間計算を組み合わせることで、ブラウザの動作状況に左右されない高精度なタイマーアプリを実現できました。自動切り替え機能により、ポモドーロテクニックの理想的な作業フローを技術的にサポートすることで、真に実用的なツールとなっています。
特に重要だったのは、単なる時間計測ツールではなく、ユーザーの作業体験全体を考慮した設計にすることでした。技術的な精度とUXの両立により、日常的に使い続けられるサービスを開発できたと考えています。
サービスはこちら
免責事項
本サービスは娯楽目的で提供されており、時間管理の効果には個人差があります。重要な業務や試験等での使用は、事前に十分なテストを行った上で自己責任でお願いいたします。
