はじめに - なぜ作ったのか
ADHDの自分には普通のToDoリストが効かない。
「やらなきゃ」という曖昧な義務感では動けない。「やらないと誰かが死ぬ」という明確な損失が必要だった。
そこで作ったのがHOSTAGE。
"Your negligence kills."(あなたの怠慢が、誰かを殺す)
培養槽の中で眠るバーチャルキャラクター。彼・彼女の命はあなたのタスク消化状況に直結している。期限を破ればそれは衰弱し、画面は血のように赤く染まり、システムエラーが走る。習慣を完了すれば回復する。
これは生産性ツールではない。心理戦だ。
デモ・ソースコード
- デモ(セットアップ不要): https://hostage-app.xyz/demo
- 本番環境: https://hostage-app.xyz
- GitHub: https://github.com/Orve/Hostage
- Orynth AI: https://www.orynth.dev/projects/hostage-solana-horror-productivity-dap
コンセプトと心理学的背景
なぜ「ホラー」なのか?
行動経済学の研究によれば、人間の損失回避の動機は利益追求の約2倍強い(プロスペクト理論)。
従来のタスク管理アプリは「タスクを完了すると嬉しい」という利益追求型。しかし、ADHDや先延ばし癖がある人間にとって、この程度の動機では不十分。
HOSTAGEは逆のアプローチ:
- タスクを放置するとキャラクターが死ぬ(損失)
- UIが崩壊し、視覚的に不快になる(損失)
- 習慣を完了すると回復する(損失回避)
キャラクターの死は、脳が「自分の責任による損失」として認識する。これが罪悪感を生み、強力な行動動機となる。
主要機能
🎮 ゲームメカニクス
タスク完了 → HP回復(優先度連動)
| 優先度 | 回復量 |
|---|---|
| Low | +3 HP |
| Medium | +5 HP |
| High | +8 HP |
| Critical | +12 HP |
習慣完了 → HP回復 + ストリーク
- 固定 +5 HP
- 連続達成日数をトラッキング
- 「🔥 7日連続達成!」のようなフィードバック
時間減衰 → 放置するほど加速的にダメージ
ダメージ = (経過時間²) × 0.5
1時間放置 → 0.5 ダメージ
10時間放置 → 50 ダメージ
24時間放置 → 288 ダメージ
💀 Death & Purge(死と廃棄)
HPが0になると被験体は死亡。ユーザーには2つの選択肢が提示される:
- Reboot(蘇生): HP50で復活、同じ被験体を継続
- Purge(検体廃棄): 完全削除し、新規被験体を作成
「失敗作を消して最初からやり直す」という選択肢が、心理的なリセット効果を生む。
📱 PWA対応(ネイティブアプリ体験)
スマートフォンの「ホーム画面に追加」で、アドレスバーのない全画面表示が可能。
- オフライン対応(Service Worker)
- プッシュ通知対応(将来実装予定)
- インストール不要、ブラウザから直接追加
🌐 多言語対応(i18n)
英語・日本語の完全対応。ブラウザ言語を自動判定し、手動切り替えも可能。
翻訳の設計思想: 直訳ではなく、SF/サイバーパンクの世界観を維持した意訳を採用。
| English | 直訳(NG) | SF調意訳(採用) |
|---|---|---|
| Sync Tasks | タスク同期 | 外部記憶同期 |
| Subject | 対象 | 被験体 |
| Signal Lost | 信号喪失 | 反応消失 |
| Your negligence has consequences | あなたの怠慢には結果がある | 怠惰には代償が伴う |
技術スタック
┌─────────────────────────────────────┐
│ Frontend (Vercel) │
│ Next.js 14 / App Router / PWA │
└──────────────┬──────────────────────┘
│ HTTPS
▼
┌─────────────────────────────────────┐
│ Backend (Railway) │
│ FastAPI / Python 3.11 │
└──────────────┬──────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ Database (Supabase) │
│ PostgreSQL / Auth / Realtime │
└─────────────────────────────────────┘
選定理由
| 技術 | 選定理由 |
|---|---|
| Next.js 14 | App RouterでSSR/CSRの最適化、PWA対応が容易 |
| FastAPI | 型安全(Pydantic v2)、非同期処理、自動ドキュメント生成 |
| Supabase | PostgreSQL + 認証 + リアルタイム購読を一括提供 |
| Vercel | Next.jsとの親和性、Edge Functionsでグローバル配信 |
| Railway | Pythonバックエンドのデプロイが簡単、自動スケール |
アーキテクチャの変遷
当初はNotion APIと連携し、ユーザーの既存タスクを読み取る設計だった。しかし以下の理由でSupabase完結型に移行:
- 仕様の不確定性: 「Notionのどのプロパティをどう同期するか」が未定義のまま肥大化
- 外部依存リスク: APIレート制限、トークン期限切れ、仕様変更に振り回される
- ゲームバランス制御: 自前DBなら「タスク完了 = +X HP」を明確に定義可能
結果として、シンプルで堅牢なシステムが完成した。
CI/CDパイプライン
GitHub Actionsによる自動品質チェックを実装。
# .github/workflows/ci.yml
jobs:
backend-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install -r requirements.txt
- run: python -m compileall app/
frontend-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: cd frontend && npm ci
- run: cd frontend && npm run build
env:
NEXT_PUBLIC_API_BASE: "http://localhost:8000"
NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }}
効果:
- デプロイ前にビルドエラーを検知
- 依存関係の不整合(存在しないバージョン指定など)を事前発見
- 導入後のデプロイ失敗率: 0%
実装で最も苦労した点
1. Horror UI実装:「不快だけど目を離せない」絶妙なライン
一番こだわったのは**「ギリ不快で、ギリ目をそらさないホラー演出」**の調整。
CRTスキャンライン
.scanlines::before {
content: " ";
display: block;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
background: linear-gradient(
to bottom,
rgba(18, 16, 16, 0) 50%,
rgba(0, 0, 0, 0.25) 50%
);
background-size: 100% 4px;
z-index: 50;
pointer-events: none;
}
グリッチテキストアニメーション
@keyframes glitch-anim-1 {
0% { clip-path: inset(20% 0 80% 0); transform: translate(-2px, 1px); }
20% { clip-path: inset(60% 0 10% 0); transform: translate(2px, -1px); }
40% { clip-path: inset(40% 0 50% 0); transform: translate(-2px, 2px); }
60% { clip-path: inset(80% 0 5% 0); transform: translate(2px, -2px); }
80% { clip-path: inset(10% 0 70% 0); transform: translate(-1px, 1px); }
100% { clip-path: inset(30% 0 50% 0); transform: translate(1px, -1px); }
}
.glitch-text::before {
left: 2px;
text-shadow: -1px 0 #ff00c1;
animation: glitch-anim-1 2s infinite linear alternate-reverse;
}
.glitch-text::after {
left: -2px;
text-shadow: -1px 0 #00fff9;
animation: glitch-anim-1 2s infinite linear alternate-reverse;
}
HPに応じた動的UI崩壊
const hpPercent = (pet.hp / pet.max_hp) * 100;
const isDead = pet.status === 'DEAD';
{isDead ? '† SIGNAL LOST †' : pet.name}
調整に時間がかかったポイント:
- グリッチの頻度(激しすぎると不快、弱すぎると印象に残らない)
- 色の選択(緑→黄→赤の段階的変化)
- モバイル端末でのパフォーマンス(60fps維持)
2. HP減衰システム:時間でじわじわ死んでいく
from datetime import datetime, timezone
DECAY_COEFFICIENT = 0.5
def calculate_time_decay(pet: dict) -> dict:
"""
指数関数的にダメージが増加(放置するほど加速度的に危険)
"""
if pet['status'] == 'DEAD':
return pet
last_checked_str = pet.get('last_checked_at')
if not last_checked_str:
return pet
try:
last_checked = datetime.fromisoformat(
last_checked_str.replace('Z', '+00:00')
)
except ValueError:
return pet
now = datetime.now(timezone.utc)
hours_passed = (now - last_checked).total_seconds() / 3600.0
if hours_passed <= 0:
return pet
damage = (hours_passed ** 2) * DECAY_COEFFICIENT
current_hp = float(pet.get('hp', 100))
new_hp = max(0.0, current_hp - damage)
updated_pet = pet.copy()
updated_pet['hp'] = new_hp
if new_hp <= 0:
updated_pet['status'] = 'DEAD'
return updated_pet
ポイント: 単純な線形減衰ではなく、二乗関数を採用。「ちょっとくらい大丈夫」という油断を許さない設計。
3. Next.js App RouterとReact Contextの境界問題
多言語対応(i18n)実装時、Server ComponentとClient Componentの境界でContextが使えずHydrationエラーが頻発。
解決策: LayoutClientラッパーを作成し、境界を明確化。
// components/LayoutClient.tsx
'use client';
import dynamic from 'next/dynamic';
import { LanguageProvider } from '@/lib/i18n/LanguageContext';
const LanguageToggle = dynamic(
() => import('@/components/ui/LanguageToggle'),
{ ssr: false }
);
export default function LayoutClient({ children }: { children: React.ReactNode }) {
return (
<LanguageProvider>
<LanguageToggle />
{children}
</LanguageProvider>
);
}
Phase 2構想:Solana Dynamic NFTs
現在のHOSTAGEはWeb2アプリだが、Phase 2でSolana統合を予定。
Dynamic NFTとは?
通常のNFTは静的だが、メタデータが変化するNFT。
HOSTAGEのキャラクターをDynamic NFTにすることで:
- キャラクターの状態がNFTに反映(健康時は緑、クリティカル時は赤)
- 生死記録が永続保存(完全な履歴がブロックチェーンに刻まれる)
- Achievement NFT(7日生存、30日生存などのバッジ)
{
"name": "HOSTAGE Subject #1337",
"description": "A productivity companion whose fate depends on your discipline",
"attributes": [
{"trait_type": "Current Status", "value": "Critical"},
{"trait_type": "Days Survived", "value": 23},
{"trait_type": "Tasks Completed", "value": 156},
{"trait_type": "Deaths", "value": 2}
]
}
フィードバック募集
聞きたいこと
- 心理学的効果: 実際に使ってみて、罪悪感は動機になりましたか?
- UI/UX: ホラー演出は「不快」と「興味深い」のバランスが取れていますか?
- 技術的改善点: コードレビュー、アーキテクチャ改善の提案があれば
- Phase 2構想: Solana統合について、面白そうな機能アイデアはありますか?
おわりに
「タスク管理が続かない」という悩みを持つ方、ぜひHOSTAGEを試してみてください。
そして、「こんな機能があったらもっと怖い(やる気が出る)」というアイデアがあれば、ぜひコメントで教えてください。
"Your negligence kills."
このコンセプトが、生産性ツールの新しい可能性を開くと信じています。
関連リンク:
- 🎮 Demo: https://hostage-app.xyz/demo
- 🌐 Production: https://hostage-app.xyz
- 💻 GitHub: https://github.com/Orve/Hostage
- 🤖 Orynth AI: https://www.orynth.dev/projects/hostage-solana-horror-productivity-dap