A browser-based consequence puzzle game built around mechanical controls, telemetry, procedural thinking, and failure analysis.
BAD DECISION is an interactive control-room puzzle game where every chamber presents a machine state, a set of physical-style controls, telemetry, operating constraints, and consequences.
The game is designed around a simple idea:
Observe → Understand → Plan → Act → System Responds → Learn → Retry → Solve
Instead of relying on conventional puzzle menus, the interface presents switches, levers, breakers, dials, gauges, status messages, incident reports, and operating notes as parts of a simulated control system.
- 50 playable chambers built from handcrafted core scenarios and generated extended scenarios.
- Mechanical-style switches, levers, dials, breakers, and other actuators.
- Telemetry gauges connected to the active game state.
- Failure states with incident title, cause, report, and recovery advice.
- Victory states with move tracking and progression.
- Best-move tracking for completed chambers.
- Incident archive for recorded failures.
- Progressive unlocking of chambers.
- Declassified solution support after repeated failed attempts.
- Keyboard controls for faster interaction.
- Sound effects for switching, steam, catastrophe, and successful clearance.
- Dark and light interface themes.
- Browser-local progress persistence through
localStorage. - Responsive interface components for desktop and smaller screens.
flowchart LR
A[Main Console] --> B[Select Chamber]
B --> C[Read Briefing]
C --> D[Inspect Controls]
D --> E[Plan Sequence]
E --> F[Operate Controls]
F --> G[Telemetry Updates]
G --> H{System State}
H -->|Safe| I[Continue]
H -->|Unsafe| J[Incident]
H -->|Solved| K[Victory]
J --> L[Review Cause]
L --> M[Reset / Retry]
M --> D
K --> N[Save Progress]
N --> O[Unlock Next Chamber]
flowchart TD
A[Operator Action] --> B[Level Evaluator]
B --> C{Consequence}
C -->|Failure| D[Incident Record]
C -->|Success| E[Victory State]
C -->|Continue| F[Updated Machine State]
D --> G[Cause + Report + Advice]
G --> H[Attempt Counter]
H --> I[Retry]
E --> J[Best Move Check]
J --> K[Progress Update]
F --> L[Telemetry / Status]
Browser
│
├── React Application
│ ├── App State
│ ├── Game Modes
│ ├── Level Selection
│ ├── Progress Handling
│ └── Theme Handling
│
├── Game Engine
│ ├── Level Definitions
│ ├── Initial State
│ ├── Interactive Objects
│ ├── Telemetry Gauges
│ └── Consequence Evaluation
│
├── UI Layer
│ ├── Control Room
│ ├── Mechanical Controls
│ ├── Gauges
│ ├── Strip Chart
│ ├── Incident Modal
│ ├── Victory Modal
│ ├── Hint Modal
│ └── Incident Archive
│
├── Audio Layer
│ └── Web Audio / Game Sound Controller
│
└── Browser Storage
└── localStorage
├── Unlocked Levels
├── Best Moves
├── Incidents
├── Sound Preference
└── Attempt Counts
Each chamber is represented by a LevelDefinition.
A chamber contains:
| Component | Purpose |
|---|---|
| Level metadata | Number, title, subtitle, theme |
| Objective | Defines the required outcome |
| Briefing | Establishes the operating context |
| Warning | Highlights a critical constraint |
| Solution hint | Provides optional guidance |
| Solution steps | Describes a valid operating sequence |
| Initial state | Defines the machine starting condition |
| Interactive objects | Defines controls and available states |
| Telemetry gauges | Visualizes machine values |
| Status message | Communicates current operating state |
| Evaluator | Determines continuation, failure, or victory |
| Move limits | Controls the permitted operation cycle |
The current source combines:
- 5 core handcrafted chambers in
src/levels/data.ts - 45 extended chambers generated by
generateExtendedLevels()insrc/levels/extendedLevels.ts
Total:
5 Core Chambers
+
45 Extended Chambers
=
50 Chambers
The extended scenarios cover themes including:
- Hydrostatics and fluids
- High voltage and dielectrics
- Thermodynamics and steam
- Pneumatics and gas flow
- Nuclear and radiation systems
- Mechanical inertia
- Cryogenics and phase systems
- Chemical reaction systems
- Apex terminal facility scenarios
bad-decision/
├── index.html
├── package.json
├── bun.lock
├── tsconfig.json
├── vite.config.ts
├── metadata.json
├── .env.example
├── .gitignore
│
├── public/
│
└── src/
├── App.tsx
├── main.tsx
├── index.css
│
├── assets/
│ └── images/
│
├── audio/
│ └── sound.ts
│
├── components/
│ ├── AnalogGauge.tsx
│ ├── ControlRoom.tsx
│ ├── DeclassifiedSolutionModal.tsx
│ ├── Gauge3D.tsx
│ ├── HintModal.tsx
│ ├── IncidentArchive.tsx
│ ├── IncidentModal.tsx
│ ├── LevelVictoryModal.tsx
│ ├── MechanicalSwitch.tsx
│ ├── StripChart.tsx
│ ├── TitleMenu.tsx
│ └── TopBar.tsx
│
├── levels/
│ ├── data.ts
│ └── extendedLevels.ts
│
├── storage/
│ └── progress.ts
│
└── types/
└── game.ts
| Area | Technology |
|---|---|
| UI | React 19 |
| Language | TypeScript |
| Build Tool | Vite 8 |
| Styling | Tailwind CSS 4 + project CSS |
| Icons | Lucide React |
| Motion | Motion |
| Audio / Effects | Browser audio and canvas-confetti |
| Persistence | Browser localStorage |
| Package Lock | Bun |
| Runtime | Modern web browser |
Install one of the following:
- Node.js with npm
- Bun
A current modern browser is recommended for development and gameplay.
git clone https://github.com/Devputta/BAD-DECISION.git
cd BAD-DECISION
bun install
bun run devOpen:
http://localhost:3000
git clone https://github.com/Devputta/BAD-DECISION.git
cd BAD-DECISION
npm install
npm run devOpen:
http://localhost:3000
Using Bun:
bun run buildUsing npm:
npm run buildPreview the production build:
bun run previewor:
npm run preview| Script | Purpose |
|---|---|
dev |
Starts the Vite development server on port 3000 |
build |
Creates a production build |
preview |
Serves the production build locally |
lint |
Runs TypeScript type checking |
clean |
Removes generated build/server artifacts |
Run type checking with:
bun run lintor:
npm run lint| Key | Action |
|---|---|
1 – 9 |
Operate the corresponding available control |
R |
Reset the current chamber |
ESC |
Return to the main console / close active overlays |
Mouse and touch interaction can be used with the visible controls.
The game stores progress in the browser using localStorage.
The stored game data includes:
Unlocked levels
Best move counts
Incident history
Sound preference
Level attempt counts
The application validates stored values when loading progress and falls back to a clean default state if stored data is invalid.
No application server is required for the current game state and progress system.
To reset saved progress, clear the site's local storage for the deployed or local domain using the browser's developer tools or site-data controls.
The interface supports two visual modes:
Dark
Light
The selected theme is stored locally in the browser.
BAD DECISION is a software simulation and puzzle experience.
The scenarios use terminology and concepts inspired by industrial control systems, electrical systems, pressure systems, mechanical equipment, chemical processes, and other technical environments. The game should not be treated as an operational procedure, engineering manual, or real-world safety instruction.
Do not use the game's instructions as guidance for operating real machinery or hazardous equipment.
The project is structured around:
- State-driven interactions
- Explicit consequence evaluation
- Observable telemetry
- Clear failure reporting
- Repeatable level behavior
- Local persistence
- Small reusable UI components
- Separation between level definitions and interface components
Before release:
flowchart LR
A[Edit] --> B[Type Check]
B --> C[Production Build]
C --> D[Local Preview]
D --> E[Play Critical Paths]
E --> F[Test Failure States]
F --> G[Test Victory States]
G --> H[Test Reset / Progress]
H --> I[Deploy]
Recommended commands:
npm run lint
npm run build
npm run previewThe application is a Vite client-side application and can be deployed to static hosting or platforms that support Vite builds.
Typical production output:
dist/
The current live deployment is available at:
https://bad-decision-mjjxncbwu.vercel.app/
The repository contains .env.example.
Before adding environment variables, verify that the application actually consumes them. Do not commit real secrets.
The current game source does not require a server-side API for its core gameplay or local progress system.
When modifying the project:
- Keep game state transitions deterministic where possible.
- Keep level-specific rules inside level definitions.
- Avoid putting chamber-specific logic into reusable UI components unless necessary.
- Preserve keyboard accessibility when adding controls.
- Validate failure and victory conditions after changing a level.
- Avoid committing secrets or local environment files.
- Run type checking and a production build before opening a pull request.
- Keep dependency changes intentional and documented.
This project is distributed under the MIT License.
See LICENSE for the complete license text.
Security-related guidance is available in SECURITY.md.