Underwater Dive Simulation — Claude Code Build Prompt
Paste this entire document into a fresh Claude Code session at the project root. It is the source-of-truth spec.
---
Intent
Build a mobile-first 3D underwater dive simulation inspired by Deep Dive Dubai's 60m descent format. First-person, three selectable modes (Scuba / Freedive / Snorkel), seven depth tiers with signature objects, EN/AR narration toggle, deploys to Vercel. Generic underwater ruins theme — no brand affiliation. This is a personal portfolio piece.
---
Tech Stack (non-negotiable)
- **Framework:** Vite + React 18 + TypeScript
- **3D:** `three`, `@react-three/fiber`, `@react-three/drei`
- **Physics:** `@react-three/rapier`
- **State:** Zustand
- **Styling:** Tailwind CSS
- **i18n:** lightweight in-house (just EN/AR JSON dictionaries, no library needed)
- **Audio:** native HTMLAudioElement, no library
- **Deploy:** Vercel
No GLTF asset packs. All 3D objects built from Three.js primitives + drei helpers (Box, Cylinder, Sphere, RoundedBox, Text, etc.) styled with materials. Keep deps light.
---
File Structure
```
/
├── public/
│ ├── audio/
│ │ ├── ambient/ # water-loop.mp3, surface.mp3
│ │ ├── sfx/ # regulator.mp3, heartbeat.mp3, bubble.mp3, wave.mp3, alarm.mp3
│ │ ├── en/ # tier-03.mp3 through tier-60.mp3, intro.mp3
│ │ └── ar/ # tier-03.mp3 through tier-60.mp3, intro.mp3
│ └── textures/ # caustics, normal maps (procedural fallbacks if missing)
├── src/
│ ├── main.tsx
│ ├── App.tsx
│ ├── i18n/
│ │ ├── en.json
│ │ └── ar.json
│ ├── state/
│ │ └── useDiveStore.ts # zustand: mode, lang, depth, air, breath, tier, paused
│ ├── scene/
│ │ ├── Scene.tsx # R3F Canvas wrapper
│ │ ├── Water.tsx # surface + underwater shader/fog
│ │ ├── Diver.tsx # camera controller + physics body
│ │ ├── Lighting.tsx # sun, god rays, depth-falloff
│ │ ├── Caustics.tsx # animated caustic projection
│ │ ├── Particles.tsx # suspended particles + bubble streams
│ │ ├── Fish.tsx # boids flocking system
│ │ ├── Kelp.tsx # instanced swaying seaweed
│ │ ├── Megafauna.tsx # turtle/ray with spline path
│ │ └── tiers/
│ │ ├── Tier03.tsx # motorbike, ATM, chess table
│ │ ├── Tier06.tsx # wet bell air chamber
│ │ ├── Tier12.tsx # depth marker, ruin column
│ │ ├── Tier18.tsx # foosball table, treasure chest
│ │ ├── Tier30.tsx # apartment (kitchen, bedroom, music room)
│ │ ├── Tier40.tsx # garage, cars, arcade cabinets
│ │ └── Tier60.tsx # bottom — sand floor, anchor, signage
│ ├── ui/
│ │ ├── ModePicker.tsx # entry screen: 3 mode cards + lang toggle
│ │ ├── HUD.tsx # depth meter + mode gauge + lang + replay narration
│ │ ├── Joystick.tsx # left-thumb virtual stick
│ │ ├── LookPad.tsx # right-thumb camera pan
│ │ ├── ActionButtons.tsx # ascend/descend/interact
│ │ ├── TierToast.tsx # tier-entry narration card
│ │ └── BlackoutOverlay.tsx # freedive blackout fade
│ ├── hooks/
│ │ ├── useDepthTier.ts # derives current tier from y position
│ │ ├── useAudio.ts # audio playback w/ duck/fade
│ │ ├── useNarration.ts # plays tier audio in current lang
│ │ └── useModeRules.ts # per-mode physics & limits
│ └── lib/
│ ├── boids.ts # flocking math
│ └── easing.ts
├── tailwind.config.ts
├── vite.config.ts
├── tsconfig.json
├── vercel.json
└── package.json
```
---
Build Phases
Work in this order. Commit after each phase.
Phase 1 — Skeleton
Phase 2 — Mode Picker
Phase 3 — Diver + Water + Controls
Phase 4 — Depth + Tier System
Phase 5 — Mode Rules (Medium realism)
Phase 6 — Tier Content
- **3m:** sunken motorbike (low-poly), ATM cabinet, chess table with pieces. Stone tiles + algae patches.
- **6m:** wet bell chamber — half-sphere with air pocket at top, interior bench. Light cone from above.
- **12m:** stone archway + depth marker buoy chain. Ruin column.
- **18m:** foosball table, half-buried treasure chest (closed, glow inside).
- **30m:** open-roof apartment block — kitchen (table, fridge silhouette), bedroom (bed frame), music room (piano, guitar shapes).
- **40m:** garage interior — two cars (boxy 60s/70s silhouettes), arcade cabinets in a row, workbench with tools.
- **60m:** sand floor, large boat anchor, "60M" stone plaque, scattered shells.
All built from Three.js primitives. Stylized — not photoreal. Slight algae overlay material on everything (greenish tint + noise).
Phase 7 — Atmosphere
Phase 8 — Life
Phase 9 — Audio
Phase 10 — Polish + Deploy
---
i18n Content
Tier narration script — write placeholder text in both `en.json` and `ar.json`. Sample tone:
> "**3 metres.** You're at the depth of a sunken motorbike. Reach out — that's the ATM where someone, somewhere, once withdrew cash."
> "**60 metres.** The bottom. Light from the surface is a memory. Pressure is seven atmospheres. Few people on earth have stood here on one breath."
Write all seven tiers + an intro line per mode. Khurram will pass these through ElevenLabs separately and drop the mp3s into the audio folders.
UI strings to translate: mode names, "Dive", "Ascend", "Descend", "Interact", "Replay narration", "Air", "Breath", "Surface", "Depth", and the blackout message ("You blacked out. Returning to surface.").
---
Performance Budget (mobile-first)
- Target: iPhone 12 / Pixel 6 and newer, sustained 30fps
- Max ~50k tris in scene at once
- Instanced meshes for particles, fish, kelp
- Limit shadow casters to diver + 2 hero objects per tier
- Lazy-load distant tier components (suspend below current tier ± 1)
- No real-time reflections — fake with subtle env map
- Frame-skip non-critical animations when below 25fps
---
Defaults Locked
1. Scuba depth cap = 40m (no cert selector)
2. Freedive blackout = respawn at surface, no death state
3. Performance target = iPhone 12+ / recent Android
4. All 3D from Three.js primitives, no GLTF imports
5. Tier narration plays on entry, replayable from HUD
6. localStorage for mode + lang, no progression/achievements
7. Caustics + depth fog + handheld torch ≥40m
Name TBD — splash and meta tags use placeholder `Dive Sim` for now. Owner will rename later.
---
Out of Scope (do not build)
- User accounts / auth
- Multiplayer
- Achievements / progression beyond current session
- Real DDD branding, logos, or copy
- GLTF asset imports
- WebXR / VR mode
- Analytics
- PWA installability (unless trivial)
---
Kick-off Instructions for Claude Code
1. Scaffold the Vite + R3F project per file structure above
2. Run Phase 1 end-to-end, then check in with the user before continuing
3. After each phase, summarise what shipped, what's deferred, and any decisions you took
4. If anything in this spec conflicts with itself or with a real constraint you hit, stop and flag it — don't paper over it
5. Mobile-first means: every PR should be testable in a phone viewport (375×812 or similar) without horizontal scroll or HUD overflow