Kimi swarm project: tilt-marble-maze
Find a file
hermes d1a94f70ed
All checks were successful
Build & Test / build (push) Successful in 39s
ci: re-run via runner pickup verification
2026-09-26 20:07:02 -07:00
.forgejo/workflows fix: use full action URLs for Forgejo compatibility 2026-06-03 20:27:11 -07:00
app ci: add jsdom devDep so vitest can resolve test environment 2026-09-15 09:09:39 -07:00
auto-triage gsd: pre-implementation lint/format cleanup 2026-05-17 07:53:52 -07:00
.dockerignore fix #6: M006: Docker and deployment configuration 2026-05-17 07:59:55 -07:00
.gitignore gsd: pre-implementation lint/format cleanup 2026-05-16 15:37:55 -07:00
.quickfix-run.log fix #3: M003 audio SFX hooks (countdown, jump) and state fields 2026-06-16 11:11:35 -07:00
docker-compose.yml fix #6: M006: Docker and deployment configuration 2026-05-17 07:59:55 -07:00
Dockerfile fix #6: M006: Docker and deployment configuration 2026-05-17 07:59:55 -07:00
lefthook.yml chore: add lefthook pre-commit hooks 2026-06-03 17:36:29 -07:00
nginx.conf fix #6: M006: Docker and deployment configuration 2026-05-17 07:59:55 -07:00
package.json fix #3: M003 audio system — add audio param to updateGame, add root package.json 2026-06-16 11:09:06 -07:00
plan.md init: Kimi swarm project setup 2026-05-07 21:22:29 -07:00
README.md docs: add README 2026-05-10 06:57:04 -07:00

Marble Rush: Arena

A physics-based tilt marble maze game built with React, TypeScript, and a custom Canvas 2D engine.

Navigate a marble through intricate maze-like tracks, tilting the playing field to roll, jump, and boost your way past obstacles. Collect coins and gems, grab power-ups, race against the clock, and conquer three handcrafted worlds.


🎮 Game Overview

Marble Rush: Arena is a web-based marble racing game that combines classic labyrinth-tilt mechanics with modern game design. The game features:

  • Physics-driven marble movement with acceleration, friction, wall bouncing, and ice surfaces
  • Device tilt controls for mobile play — physically tilt your phone to steer
  • Touch joystick with on-screen jump and boost buttons
  • Keyboard controls (WASD / Arrow keys, Space, Shift)
  • 3 themed levels: Jungle Temple, Crystal Cave, Volcano Core
  • Multiple obstacle types including spinners, boulders, pendulums, collapsing platforms, spikes, magnetic fields, gravity wells, and speed gates
  • 5 power-ups: Speed Boost, Shield, Magnet, Freeze, Wall Jump
  • Collectibles: Coins (score), Gems (bonus score), Time Bonuses (reduce timer)
  • Checkpoint system with respawn on death
  • Star ratings based on par time performance (1–3 stars)
  • Energy boost mechanic — expend energy for bursts of speed
  • Particle effects, screen shake, marble trail, mini-map, and full HUD

🚀 Quick Start

Prerequisites

  • Node.js ≥ 20
  • npm ≥ 9

Install & Run

# Navigate to the app directory
cd app

# Install dependencies
npm install

# Start the dev server (opens on http://localhost:3000)
npm run dev

Build for Production

npm run build

The output is written to app/dist/ and can be served by any static file server.

Preview Production Build

npm run preview

Other Scripts

Command Description
npm run dev Start Vite dev server (port 3000)
npm run build TypeScript check + production build
npm run preview Preview the production build locally
npm run typecheck TypeScript type-check (no emit)
npm run lint Run ESLint
npm run test Run test suite (placeholder)

🎯 Controls

Keyboard

Key Action
W / Arrow Up Tilt forward / up
A / Arrow Left Tilt left
S / Arrow Down Tilt backward / down
D / Arrow Right Tilt right
Space Jump / Wall Jump
Shift Boost (uses energy)

Touch (Mobile)

Area Action
Left side of screen Joystick (tilt)
Right side — upper half Jump button
Right side — lower half Boost button

Tap and drag the joystick to steer. The jump and boost buttons are static touch zones on the right side of the screen.

Device Tilt (Mobile)

On supported devices, the game uses the device orientation API. Tilt your phone to steer the marble. Tilt sensitivity and inversion can be configured in settings. iOS requires explicit permission via the DeviceOrientationEvent.requestPermission() API.


🧩 Game Mechanics

Marble Physics

  • The marble accelerates based on input tilt direction
  • Friction slows the marble when no input is applied
  • Colliding with walls produces a bounce with energy damping
  • The marble slides along walls with friction
  • Ice tiles drastically reduce friction, causing the marble to slide
  • Boost pads launch the marble in its current velocity direction
  • Airborne gravity applies after 2 frames of no ground contact

Obstacles

Type Description
Spinner Rotating red disc with blades — contact kills
Boulder Heavy rolling ball that patrols a path
Pendulum Swinging spiked ball on a chain
Collapsing Platform Platform that breaks after being stepped on (respawns)
Spikes Periodically retracting floor spikes
Speed Gate Reduces your timer by 2 seconds on contact
Magnetic Field Pulls the marble toward its center
Gravity Well Stronger pull — can crush the marble at close range
Moving Platform Oscillating platform the marble can ride

Power-ups

Power-up Color Duration Effect
Speed Green ~3s Instant velocity burst + speed trail
Shield Cyan ~5s Temporary invincibility (cyan glow)
Magnet Gold ~4s Pulls nearby coins and gems toward the marble
Freeze Silver ~3s Slows the marble by 50% (useful for precision sections)
Wall Jump Orange ~4s Enables wall jumping off vertical surfaces

Collectibles

Type Score Description
Coin 10 Gold spinning disc
Gem 50 Blue diamond — rarer, higher value
Time Bonus -2s Green timer reduction orb

Scoring & Stars

  • Score is accumulated from coins and gems
  • Death penalty: −50 points per death
  • Stars are awarded at level completion based on par time:
    • ⭐⭐⭐ ≤ 80% of par time
    • ⭐⭐⭐ ≤ 100% of par time
    • ⭐⭐ ≤ 130% of par time
    • ⭐ > 130% of par time

Energy System

  • Energy bar (100 max) is consumed when holding Boost
  • Energy regenerates slowly over time
  • Boost increases max speed and reduces friction

🌍 Levels

The game currently features 3 handcrafted levels:

# Name Theme Par Time Description
1 Jungle Temple Ancient ruins 45s Introductory maze with basic obstacles
2 Crystal Cave Icy caverns 55s Ice tiles, pendulums, magnetic fields
3 Volcano Core Lava tunnels 60s Dense obstacles, spikes, gravity wells

Each level includes:

  • A winding maze-like wall layout
  • Multiple obstacle types
  • Coins, gems, and at least one time bonus
  • 3 checkpoints for respawn
  • Hidden collectible routes
  • Boost pads for shortcuts

🗂️ Project Structure

tilt-marble-maze/
├── app/                          # Application root
│   ├── public/                   # Static assets
│   │   ├── marble-diffuse.jpg    # Marble texture (diffuse)
│   │   ├── marble-normal.jpg     # Marble texture (normal map)
│   │   ├── hero-bg-void.jpg      # Home page background
│   │   └── track-glow-overlay.png
│   ├── src/
│   │   ├── main.tsx              # App entry point (HashRouter)
│   │   ├── App.tsx               # Route definitions
│   │   ├── index.css             # Tailwind + CSS variables
│   │   ├── lib/utils.ts          # shadcn utility
│   │   ├── types/
│   │   │   └── game.ts           # All TypeScript game type definitions
│   │   ├── game/                 # Custom Canvas 2D game engine
│   │   │   ├── engine.ts         # Core game loop, state machine, update logic
│   │   │   ├── physics.ts        # Marble physics, collision detection, vector math
│   │   │   ├── levels.ts         # Level definitions (3 levels)
│   │   │   ├── obstacles.ts      # Obstacle factory functions
│   │   │   ├── controls.ts       # Keyboard, touch, and tilt input handling
│   │   │   ├── renderer.ts       # Canvas 2D rendering (walls, obstacles, effects)
│   │   │   ├── hud.ts            # HUD overlay (timer, speed, score, mini-map)
│   │   │   ├── particles.ts      # Particle system (trails, explosions, confetti)
│   │   │   └── powerups.ts       # Power-up pickup logic, effects, and rendering
│   │   ├── components/           # React UI components
│   │   │   ├── Layout.tsx        # Page layout shell
│   │   │   ├── Navbar.tsx        # Top navigation
│   │   │   ├── Footer.tsx        # Page footer
│   │   │   └── ui/               # shadcn/ui primitives
│   │   └── pages/                # Route pages
│   │       ├── Home.tsx          # Landing page (Canvas marble animation + GSAP)
│   │       ├── Play.tsx          # Main game canvas + game state UI
│   │       ├── Modes.tsx         # Game mode selection page
│   │       ├── Leaderboard.tsx   # 3D leaderboard (Three.js / R3F)
│   │       └── HowToPlay.tsx     # Controls and mechanics guide
│   ├── dist/                     # Production build output
│   ├── package.json
│   ├── vite.config.ts
│   ├── tailwind.config.js
│   ├── tsconfig.json / tsconfig.app.json
│   ├── components.json           # shadcn/ui config
│   └── index.html                # HTML entry point
├── .forgejo/workflows/ci.yml     # CI pipeline (type-check, lint, build, test)
├── .gitignore
└── README.md                     # You are here

🏗️ Architecture

Game Engine

The game uses a custom 2D Canvas-based engine (no game framework). The core loop runs via requestAnimationFrame:

  1. Input → Controls.getInput() returns the current InputState (tilt direction, jump/boost flags)
  2. Update → updateGame() advances the physics, processes collisions, collects items, updates particles, and manages game state
  3. Render → renderGame() draws the level, marble, particles, and obstacles onto a <canvas> element
  4. HUD → drawHUD() overlays timer, speed meter, score, energy bar, power-up slots, mini-map, and touch controls

State Machine

The game follows a simple state machine:

start → countdown → playing ↔ paused → levelcomplete / gameover
  • start: Initial state, waiting for player input
  • countdown: 3-2-1-GO countdown animation
  • playing: Active gameplay
  • paused: Game paused (overlay menu)
  • levelcomplete: Level finished — show star rating and score
  • gameover: Currently functions as respawn from last checkpoint

Tech Stack

Technology Purpose
React 19 UI framework
TypeScript Type-safe development
Vite 7 Build tool and dev server
Canvas 2D Game rendering (custom engine)
Tailwind CSS Styling and design system
shadcn/ui UI component primitives
Framer Motion Page/UI animations
GSAP Landing page scroll animations
Three.js / R3F 3D backgrounds (leaderboard, how-to-play)
React Router Client-side routing (HashRouter)

🧪 CI/CD

The project includes a Forgejo CI workflow (.forgejo/workflows/ci.yml) that runs on every push/PR:

  1. Install — npm ci with Node.js 20
  2. TypeScript check — tsc --noEmit
  3. Lint — eslint (continue on error)
  4. Build — vite build
  5. Test — placeholder test runner

🧭 Current Status

  • ✅ Core gameplay loop working with physics, collisions, rendering
  • ✅ 3 playable levels with distinct themes
  • ✅ Keyboard, touch, and tilt input support
  • ✅ Full HUD, particles, screen shake, mini-map
  • ✅ Power-up and collectible system
  • ✅ Level-complete screen with star ratings
  • ✅ Landing page, how-to-play guide, modes page, leaderboard UI
  • 🚧 Game mode switching (Time Attack/Adventure/Endurance/Boss Rush) — UI exists, game logic scoped to Time Attack
  • 🚧 Leaderboard persistence — UI renders but data is static/mock
  • 🚧 Daily Challenge — UI concept, not implemented
  • 🚧 Tests — placeholder only; no unit/integration tests yet

🔧 Development

Adding a New Level

  1. Define a new LevelTheme in app/src/game/levels.ts
  2. Create a level function (e.g., level4() ) returning a LevelData object
  3. Add the case to getLevel() and increment getLevelCount()
  4. Design the maze using walls[], place obstacles[], collectibles[], powerups[], and checkpoints[]

Adding a New Obstacle

  1. Add the type string to Obstacle.type in app/src/types/game.ts
  2. Create a factory function in app/src/game/obstacles.ts
  3. Add update logic in the updateObstacles() switch in app/src/game/physics.ts
  4. Add collision handling in checkObstacleCollision() → engine.ts's obstacle switch
  5. Add rendering in the drawObstacles() switch in app/src/game/renderer.ts

📄 License

This project is provided for educational and personal use. See the repository owner for licensing details.