惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
博客园 - Franky
大猫的无限游戏
大猫的无限游戏
博客园 - 三生石上(FineUI控件)
量子位
博客园 - 聂微东
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
SegmentFault 最新的问题
Apple Machine Learning Research
Apple Machine Learning Research
爱范儿
爱范儿
V
Visual Studio Blog
雷峰网
雷峰网
T
Tailwind CSS Blog
宝玉的分享
宝玉的分享
Blog — PlanetScale
Blog — PlanetScale
有赞技术团队
有赞技术团队
博客园 - 叶小钗
Microsoft Azure Blog
Microsoft Azure Blog
T
The Blog of Author Tim Ferriss
U
Unit 42
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
小众软件
小众软件
阮一峰的网络日志
阮一峰的网络日志
Y
Y Combinator Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Building "Customer Escape Room": A Voice-Powered Game Tha...
Harish Kotra (he/him) · 2026-06-04 · via DEV Community

"Can you survive being a customer?"

At every hackathon, teams build the same thing: chatbots, AI agents, customer support copilots. All of them try to fix customer experience. None of them force you to feel what broken customer experience is actually like.

So I built the opposite. A game.


The Idea

Customer Escape Room is a mobile-first, voice-powered game where players must escape increasingly absurd customer service nightmares. Think Portal meets The Stanley Parable meets your worst call center experience.

Six levels. Six NPCs. Six unique mechanics. All connected by one question: can you survive being a customer?


The Tech Stack

Technology Purpose
Next.js 16 (App Router) Full-stack framework
TypeScript Type safety
Tailwind CSS v4 Styling
Framer Motion Animations
shadcn/ui Component library
MediaRecorder API Voice recording
OpenAI Whisper Speech-to-text
React Context + useReducer State management

Architecture

┌──────────────────────────────────────────┐
│               Page Layer                  │
│  /  (Game Hub)      /game/judge (Demo)   │
├──────────────────────────────────────────┤
│            Game Engine                    │
│  ┌────────────────────────────────────┐  │
│  │  Level 1  │  Level 2  │  Level 3  │  │
│  │  Level 4  │  Level 5  │  Level 6  │  │
│  └────────────────────────────────────┘  │
├──────────────────────────────────────────┤
│        Shared Components                 │
│  VoiceInput  NPCDialogue  ScoreDisplay   │
│  Achievements  ShareCard  LevelComplete  │
├──────────────────────────────────────────┤
│              State Layer                 │
│  GameProvider → useReducer → Context     │
├──────────────────────────────────────────┤
│               API Layer                  │
│  /api/ai  →  OpenAI / Featherless       │
│  /api/ai  →  Whisper Transcription      │
└──────────────────────────────────────────┘

Enter fullscreen mode Exit fullscreen mode


The 6 Levels

Level 1: The Refund Maze

The player is trapped in a corporate maze. The Refund Robot NPC obsesses over "Policy 42(a)(3)(b)(ii)" — a fictional policy that keeps getting more nested the deeper you go. The player must ask for Form R-1, fill it out, then demand supervisor escalation to escape.

Mechanic: Bureaucratic layer navigation. Each stage requires specific keywords to advance.

Level 2: IVR Hell

A timed level. The player is stuck in an infinite phone menu system that loops back on itself. Press 1 for Billing. Press 2 for Billing Questions. Press 3 for Billing Questions About Billing. The only escape is saying "operator" or "representative" — the hidden human path.

Mechanic: Countdown timer + menu depth tracking. The hint system activates after 3+ failed attempts.

Level 3: Transfer Dungeon

The Transfer Goblin enthusiastically transfers the player to random departments, forgetting everything each time. The player must track their own story consistency — repeating yourself gets penalized. Escape by asking for "Escalations" after 5+ transfers.

Mechanic: Transfer counter + repetition detection (word-level matching against previous responses).

Level 4: Subscription Prison

Manager Dragon uses every retention tactic in the book. "Are you sure?" asked 4 times. Discount offers. Guilt trips. The player must stay firm through multiple "sure?" checkpoints without accepting any offers.

Mechanic: Persuasion gauntlet. Saying "no" or accepting offers resets progress. Only firm persistence wins.

Level 5: Chatbot Labyrinth

The Chatbot Wizard answers every question wrong with extreme confidence. The player must discover 3 hidden keywords ("human", "agent", "representative") to prove they're not a bot. Every regular input is met with creatively unhelpful responses.

Mechanic: Keyword collection. Misunderstanding counter unlocks progressive hints.

Level 6: Karen Boss Fight

The final boss. Karen Queen interrupts, contradicts herself, escalates unreasonably, and demands everything for free. The player must use empathy words ("understand", "sorry", "help") to de-escalate without triggering more rage.

Mechanic: De-escalation score. Empathy words reduce anger; demanding or fighting back triggers interruptions.


Level Design Pattern

Every level follows the same architecture:

type Stage = "intro" | "stage1" | "stage2" | "escaped"

// Stage data with deterministic responses
const STAGE_DATA: Record<Stage, StageConfig> = {
  intro: {
    npc: "...NPC dialogue...",
    advanceOn: ["keyword1", "keyword2"],
    advanceTo: "stage1",
    hint: "Hint for confused players"
  },
  // ...
}

function handleTranscript(text: string) {
  // 1. Detect confused input ("i don't know", "help")
  if (isConfused(text)) return showHint()

  // 2. Check for advancement keywords
  if (matchesKeyword(text, currentStage.advanceOn)) {
    return advanceStage(currentStage.advanceTo)
  }

  // 3. Redirect to objective
  return showRedirectMessage()
}

Enter fullscreen mode Exit fullscreen mode

This pattern ensures:

  • No random responses — every NPC line is stage-appropriate
  • Confused players get help — "i don't know" always shows a hint
  • Clear progression — keywords gate advancement
  • Graceful failure — off-script input redirects to the objective

Voice System

The voice system uses the native MediaRecorder API:

// src/lib/voice.ts
export function useVoice() {
  const startRecording = async () => {
    const stream = await navigator.mediaDevices.getUserMedia({ audio: true })
    const mediaRecorder = new MediaRecorder(stream)
    // ... record audio chunks
  }

  const stopRecording = async () => {
    // ... create blob, send to /api/ai with X-Transcribe
    const text = await transcribeAudio(blob)
    return text
  }
}

Enter fullscreen mode Exit fullscreen mode

The VoiceInput component wraps this with a toggle:

<VoiceInput onTranscript={handleTranscript} />
// Shows mic button + "⌨️ Type instead" toggle

Enter fullscreen mode Exit fullscreen mode

Falls back gracefully:

  • No microphone → shows text input only
  • No API key → simulates transcription with context-aware phrases
  • Error → shows text input as fallback

AI Abstraction Layer

The API route handles both dialogue generation and transcription through a unified endpoint:

// /api/ai handles:
// 1. Chat completions (dialogue)
// 2. Audio transcriptions (Whisper)

const provider = process.env.AI_PROVIDER || "openai"
const baseUrl = provider === "featherless"
  ? "https://api.featherless.ai/v1"
  : "https://api.openai.com/v1"

Enter fullscreen mode Exit fullscreen mode

Provider switching via env vars:

AI_PROVIDER=openai           # Uses gpt-4o-mini
AI_PROVIDER=featherless      # Uses Llama 3.1 8B

Enter fullscreen mode Exit fullscreen mode


State Management

Game state uses React Context with useReducer:

type Action =
  | { type: "SET_PHASE"; phase: GamePhase }
  | { type: "SET_LEVEL"; levelId: LevelId }
  | { type: "ADD_SCORE"; levelId: LevelId; category: ScoreCategory; amount: number }
  | { type: "ADD_ACHIEVEMENT"; achievementId: string }
  | { type: "RESET_GAME" }

// Scores are calculated cumulatively
// Achievements are checked and dispatched by level logic
// Game phase controls which screen renders

Enter fullscreen mode Exit fullscreen mode


Scoring & Ranks

6 Categories × 6 Levels × 100 points = 3,600 max

Categories:
  patience       - Staying calm under absurdity
  problemSolving - Finding the escape path
  persistence    - Not giving up
  empathy        - Showing understanding (critical for Level 6)
  escapeSpeed    - Completing efficiently
  negotiation    - Arguing effectively

Ranks:
  🥉 Casual Complainer    (0+ pts)
  🥈 Support Veteran      (300+ pts)
  🥇 Customer Champion    (600+ pts)
  👑 Escape Legend        (900+ pts)

Enter fullscreen mode Exit fullscreen mode


Judge Demo Mode

One of the most important features: /game/judge drops judges directly into Level 2 (IVR Hell) with zero friction.

// /game/judge/page.tsx
<GameEngine initialLevel={2} />

Enter fullscreen mode Exit fullscreen mode

No signup. No onboarding. Ten seconds from scanning a QR code to playing.


What I'd Add Next

  1. Multiplayer — Socket.io is installed. 4-8 players in a shared escape room with voice cooperation.
  2. Dynamic AI scenarios — Use the AI provider to generate unique company names and NPC dialogue per playthrough.
  3. Supabase persistence — Leaderboards, saved scores, daily challenges.
  4. Sound design — Hold music, menu beeps, NPC voice lines.
  5. More levels — "The Data Deletion Request," "The Warranty Voidening," "The Return Policy Paradox."
  6. Share cards — Generate shareable SVG images with scores and rank.

Try It

npm install
npm run dev
# → http://localhost:3000
# → http://localhost:3000/game/judge (judge demo)

Enter fullscreen mode Exit fullscreen mode

Screenshots

How it works - demo 1

How it works - demo 2

How it works - demo 3

How it works - demo 4

Code & more: https://www.dailybuild.xyz/project/153-escape-velocity