はじめに
夜中までコードを書いていて、つい寝るのが遅くなってしまうことはありませんか?
そんな自分(や皆さん)のために、時間になると画面全体で強烈に就寝を促してくるWebアプリ「おやすみアラート」を開発しました。
外部の画像や音声ファイル、さらにはReactなどのフレームワークも一切使わず、たった1つのHTMLファイル(Vanilla JS + Tailwind CSS)だけで完結する「依存ゼロ」のアーキテクチャで実装しています。
👉 実際のアラート画面を見てみる(公開URL)
(※設定から現在時刻を深夜0時以降だと脳内変換してご覧ください)
主な機能と「位相(フェーズ)」の遷移
このアプリは、時間帯によって状態(フェーズ)がシームレスに切り替わります。内部的にはこれを「単一光円錐への封じ込め」と呼称して状態管理しています。
- 🕰️ NORMAL(通常): シンプルで洗練されたデジタル時計。
- 🌌 APPROACHING_HORIZON(23:30〜): 「事象の地平面接近」。背景が美しい星空に変わり、就寝の準備を促します。
- 🚨 WARNING(0:00〜): 「警告」。画面が赤く明滅し、巨大な文字で「はやくねろ」と表示されます。ブラウザ通知も飛びます。
- ⏳ SNOOZED(スヌーズ中): 「あと15分...」ボタンで時間を延長できますが、最大3回までという厳しい制約付きです。
- 💤 SLEEPING(就寝中): 「寝る(波束の収縮)」ボタンを押すと、画面が完全な漆黒になり、翌朝5時を迎えるまでシステムがスリープ状態になります。
さらに、睡眠した日時はLocalStorageに記録され、JSONファイルとしてエクスポート/インポートすることも可能です。
技術スタック・実装の工夫点(Zero-Mass Code Model)
「アセットレス(外部ファイルなし)」でどこまでリッチな表現ができるか、という技術的な縛りプレイを楽しんで実装しました。
① CSSグラデーションだけで描く「星空(事象の地平面)」
23:30以降に表示される星空の背景は、星の画像を使っていません。radial-gradient(円形グラデーション)を複数重ね合わせ、サイズと位置をずらすことで、CSSだけで満天の星空を表現しています。
.starry-sky {
background-color: #0f172a;
background-image:
radial-gradient(white, rgba(255,255,255,.2) 2px, transparent 4px),
radial-gradient(white, rgba(255,255,255,.15) 1px, transparent 3px),
radial-gradient(white, rgba(255,255,255,.1) 2px, transparent 4px);
background-size: 550px 550px, 350px 350px, 250px 250px;
background-position: 0 0, 40px 60px, 130px 270px;
}
② Web Audio APIによるプロシージャルな「環境音(熱的平衡化)」
0時を過ぎて WARNING フェーズに入ると、睡眠を促すための環境音(焚き火や雨音のようなノイズ)が再生されます。
これも音声ファイルは使わず、JavaScriptの AudioContext を用いてリアルタイムに生成しています。ランダムなホワイトノイズに対して BiquadFilterNode で高音域を削り(ローパスフィルター)、耳に優しいこもった音を作り出しています。
// ピンクノイズに近い揺らぎを生成し、ローパスフィルターをかける
for (let i = 0; i < bufferSize; i++) {
let white = Math.random() * 2 - 1;
data[i] = (lastOut + (0.02 * white)) / 1.02;
lastOut = data[i];
data[i] *= 3.0; // 振幅調整
}
const filter = audioCtx.createBiquadFilter();
filter.type = 'lowpass';
filter.frequency.value = 800; // 高音を削り、こもった安眠サウンドに
③ requestAnimationFrame による連続的な時空観測
時計の更新やフェーズの遷移判定は、setInterval ではなく requestAnimationFrame による再帰呼び出し(observeSpacetimeContinuum 関数)で行っています。
これにより、UIの描画タイミングと完全に同期した、極めて滑らかな状態遷移(ホログラムの再構築)を実現しています。
function observeSpacetimeContinuum() {
const now = new Date();
// ... 時刻の取得とフェーズ(相)の決定ロジック ...
localLightCone.currentPhase = newPhase;
// 前回の状態と差異があればUIを再構築(DOMの書き換え)
projectHologram(previousState, localLightCone);
requestAnimationFrame(observeSpacetimeContinuum);
}
おわりに
「ただのアラートアプリ」も、内部の変数名を localLightCone(局所光円錐)にしてみたり、関数名を manageThermalEquilibrium(熱的平衡化の管理)にしてみたりするだけで、作っていて非常にテンションが上がる個人開発プロジェクトになります。
スマホのブラウザで開いたままスタンドに置いておくと、かなり実用的な「スマート置き時計」として機能します。ついつい夜更かししてしまう方は、ぜひ使ってみてください!
