A browser-based 3D flight simulator built with Three.js and TypeScript. Fly through procedurally generated terrain, dodge obstacles, and shoot down enemies for as long as you can survive.
| Layer | Technology | Role |
|---|---|---|
| Language | TypeScript 5.9 | Type-safe source |
| 3D Engine | Three.js 0.183 | Rendering, geometry, materials, lighting |
| Build | Vite 6.4 | Dev server, HMR, production bundler |
| Audio | Web Audio API | Procedural sound effects (no asset files) |
| Storage | localStorage | High score persistence |
| Physics | Custom sphere collision | Lightweight hit detection without a physics engine |
No frameworks. No external assets. Everything — terrain, models, sounds — is generated procedurally at runtime.
┌─────────────────────────────────────────────────────────┐
│ main.ts (Game) │
│ State machine: menu → playing → gameover │
│ requestAnimationFrame loop, dt-capped │
├─────────────┬───────────────┬───────────────────────────┤
│ entities/ │ scene/ │ systems/ │
│ │ │ │
│ plane.ts │ setup.ts │ input.ts audio.ts │
│ enemy.ts │ terrain.ts │ collision.ts scoring.ts │
│ projectile │ obstacles.ts │ difficulty.ts │
│ camera.ts │ clouds.ts │ enemyManager.ts │
│ │ │ projectileManager.ts │
├─────────────┴───────────────┴───────────────────────────┤
│ ui/ utils/ │
│ hud.ts math.ts │
│ menu.ts (lerp, clamp, fbm, noise)│
└─────────────────────────────────────────────────────────┘
Game Loop (main.ts)
│
├─ InputManager ─────────── reads mouse (pointer lock) + keyboard
│
├─ PlaneState ───────────── applies input → speed, pitch, roll, position
│ └─ plane model ─────── Three.js group synced to PlaneState each frame
│
├─ TerrainManager ────────── FBM noise → chunked ground mesh, recycled behind player
├─ ObstacleManager ──────── rocks, canyon walls, birds spawned per chunk
├─ CloudManager ─────────── decorative pink clouds
│
├─ EnemyManager ─────────── spawns planes + blimps on timers, AI steering
├─ ProjectileManager ────── player & enemy bullets, lifetime + collision
│
├─ checkCollisions ──────── sphere-sphere: obstacles vs player
├─ checkCombatCollisions ── projectiles vs enemies, projectiles vs player
│
├─ ScoringSystem ────────── distance × speed + near-miss + kill bonuses
├─ DifficultySystem ─────── level = √(distance / 150), capped at 8
│
├─ ChaseCamera ──────────── follows plane, screen shake on damage
├─ HUD ──────────────────── DOM overlay: speed, health, score, boost
└─ renderer.render() ────── Three.js draws the frame
flight-sim/
├── index.html Entry point — inline styles, UI skeleton
├── package.json
├── tsconfig.json
├── vite.config.ts
└── src/
├── main.ts Game class, loop, state transitions
├── entities/
│ ├── plane.ts Player model, flight physics, boost, damage
│ ├── enemy.ts Enemy planes (2 HP) and blimps (5 HP)
│ ├── projectile.ts Bullet geometry and lifetime
│ └── camera.ts Chase camera with shake
├── scene/
│ ├── setup.ts Scene, renderer, lights, fog
│ ├── terrain.ts FBM-noise chunked terrain, recycled behind player
│ ├── obstacles.ts Rock pillars, floating rocks, canyon walls, birds
│ └── clouds.ts Procedural pink clouds
├── systems/
│ ├── input.ts Mouse (pointer lock) + keyboard, smooth lerp
│ ├── collision.ts Obstacle and combat hit detection
│ ├── scoring.ts Points, multipliers, localStorage high scores
│ ├── difficulty.ts Level scaling by distance traveled
│ ├── enemyManager.ts Spawn timers, AI update, removal
│ ├── projectileManager.ts Bullet updates and cleanup
│ └── audio.ts Web Audio oscillators and noise generators
├── ui/
│ ├── hud.ts In-game speed, health, score, boost meter
│ └── menu.ts Title screen, game over, high score table
└── utils/
└── math.ts lerp, clamp, randRange, 2D noise, FBM
| Input | Action |
|---|---|
| Mouse | Steer (pointer lock) |
| Arrow keys | Steer (smoothly interpolated alternative) |
| W / S | Throttle up / down |
| A / D | Roll left / right |
| Space | Boost (2s duration, 5s cooldown) |
| F / Enter / Left click | Fire |
Flight — Speed ranges from 30 to 120 km/h (180 during boost). The plane is bounded laterally (±32 units) and vertically (2–35 units). Scraping the ground drains 50 HP/s.
Obstacles — Rock pillars (35 dmg), floating rocks (25 dmg), canyon walls (40 dmg), and birds (15 dmg) populate the terrain. A near miss within ~5 units awards +50 points; actual collision radius is ~2 units.
Enemies — Fighter planes (2 HP, shoot at you) spawn every 4–18 seconds. Blimps (5 HP, passive) spawn every 25–45 seconds. Kill rewards scale with difficulty level.
Scoring — Base score accrues from distance × speed multiplier. Near misses and kills add bonuses. Top 10 high scores persist in localStorage under key sky-dodger-highscores.
Difficulty — Level = floor(sqrt(distance / 150)), capped at 8. Higher levels increase obstacle density, enemy spawn rates, and kill bonuses.
npm install
npm run dev # Vite dev server, auto-opens browsernpm run build # TypeScript check + Vite production build → dist/
npm run preview # Serve the production build locally- No physics engine — Collision uses sphere-distance checks. The game doesn't need rigid bodies or contact resolution, so a full physics library would be dead weight.
- No asset files — All geometry is built from Three.js primitives, terrain from FBM noise, audio from Web Audio oscillators. Zero network requests for assets means instant load.
- Chunked terrain recycling — Ground meshes are generated in 100-unit chunks and repositioned behind the player once passed, keeping memory constant.
- Input smoothing — Keyboard steering lerps toward targets rather than snapping, preventing the jerky feel of binary digital input.