Disclaimer: This project is an independent open-source fan game and tribute to classic Commodore 64 party games. It is not affiliated with, sponsored by, or endorsed by The Walt Disney Company.
A multiplayer retro light-cycle arena game inspired by the classic Commodore 64 game Ultimate Tron II (created by Oliver Stiller / Masters' Design Group and published on 64'er magazine cover disks).
Built with standard web technologies — vanilla ES Modules, a deterministic 40 FPS target-timestamp physics loop, binary WebSocket streaming, and a responsive HTML5 canvas delta renderer.
On the Commodore 64, Ultimate Tron II stood out by supporting up to 6 simultaneous players sharing a single keyboard (Q/W, C/V, M/,, arrow keys, etc.), creating fast-paced local multiplayer matches where light-cycles navigated tight grid corridors and left solid obstacle trails.
Bitcycles brings that same multiplayer mechanic to modern web browsers — supporting multiple players on a shared local keyboard as well as cross-device multiplayer over WebSockets across desktops, laptops, and mobile devices.
This project originated as an independent side project developed sporadically over several years. Recently, modern AI pair-programming tools (specifically Google's Gemini models via Antigravity) were introduced to accelerate development and modernize the architecture while balancing limited time outside of work and family commitments.
AI assistance was leveraged across several key engineering areas:
- Protocol & Loop Design: Architecting the deterministic target-timestamp physics loop and compact binary WebSocket frame format.
-
Canvas Optimization: Structuring the
$O(k)$ hybrid delta renderer andInt8Arrayspatial grid buffer. -
Testing & Verification: Implementing comprehensive test suites, including multi-cycle heap leak verifications (
node --expose-gc), headless concurrency benchmarks, and Playwright browser tests.
This approach enabled rapid architectural iteration, rigorous automated testing, and clean modular separation across both client and server subsystems.
- Original C64 Inspiration: Ultimate Tron II by Oliver Stiller / Masters' Design Group (64'er Magazine, Markt & Technik) — Lemon64 Entry
- Algorithm & Score Reference: mist64/ultimatetron2
- Fixed Timestep Game Loop Architecture: Glenn Fiedler - Fix Your Timestep!
- MDN Game Loop Engine: MDN - Anatomy of a Video Game
- MDN Resolution & Zoom Scaling: MDN - Window devicePixelRatio
tron/
├── shared/ # Isomorphic modules (Node.js & Browser)
│ ├── constants.js # Grid dimensions, speeds, colors, starting positions
│ ├── protocol.js # Typed WebSocket action & event constants
│ ├── Player.js # Pure Player model (direction stack, position, killzone)
│ └── utils.js # Shared utilities (shuffle, random, ordinal formatting)
├── server/ # Backend domain logic & match simulation
│ ├── Arena.js # 2D collision grid, simultaneous crash resolution, explosions
│ ├── Explosion.js # Particle explosion physics simulation
│ ├── GameSession.js # Target-Timestamp game loop scheduler & score calculation
│ ├── GameServer.js # In-memory registry of active game sessions
│ ├── Storage.js # Crash-safe atomic JSON snapshot persistence
│ └── wsHandler.js # Unified WebSocket server (/ws) with input sanitization
├── public/ # Frontend client assets
│ ├── javascripts/
│ │ ├── main.js # Main application orchestrator & input binding
│ │ ├── network.js # Unified WebSocket client singleton & ping heartbeat
│ │ ├── state.js # Central reactive client state store (pub/sub)
│ │ ├── Renderer.js # Hybrid delta canvas renderer with DPR scaling
│ │ ├── settings.js # User preferences & localStorage persistence
│ │ ├── theme.js # Live computed CSS variable color reader & observer
│ │ └── ui/ # Modular view controllers
│ │ ├── dropdown.js # Dropdown menu controller
│ │ ├── settingsView.js # Theme, speed, and visibility toggles
│ │ ├── lobbyView.js # Game list & creation form
│ │ ├── configView.js # Player registration & key binding setup
│ │ └── gameView.js # Scoreboard, arena display, & player position badges
│ └── stylesheets/ # Vanilla CSS design system
└── docs/plans/ # Architecture roadmap & mobile layout plans
-
Target-Timestamp Server Game Loop: Tracks absolute target time (
nextTickTime) to absorb both OS timer jitter and physics computation time without busy-polling. - Unified WebSocket Protocol: Single persistent connection handling lobby, room-scoped gameplay messaging, and in-band latency ping heartbeats.
- Input Sanitization: Server-side clamping of grid size, frame intervals, player names, and movement directions.
-
Hybrid Delta Canvas Renderer: Local
Int8Arraygrid buffer with delta cell painting ($O(k)$ per frame) and full redraws on theme or DPR changes. - Simultaneous 2-in-1 Spot Collision: Equal mutual kill credit and position rollback when two players enter the same cell in the same tick.
# 1. Install Node.js dependencies
npm install
# 2. (Optional) Setup Playwright browser binaries for automated client-side tests
npx playwright install chromium
# On Linux / WSL environments, install required OS system libraries if running browser tests:
# sudo npx playwright install-deps chromium
# 3. Start development server (default: port 3000)
npm start
# 4. Run automated test suites (server lifecycles, memory GC, Playwright browser leak tests)
npm test
# 5. Run headless concurrency benchmark (10, 25, 50 concurrent active games)
npm run benchmark
# 6. Run linter
npx eslint .MAX_ACTIVE_GAMES(Default:50): Maximum concurrent active game rooms allowed on the server. Configurable via environment variable:MAX_ACTIVE_GAMES=100 npm start
MAX_CLIENTS_PER_ROOM(Default:32): Maximum connected clients (players + spectators) per game room.- Automatic Inactivity Reaper: Rooms with zero connected clients automatically clean up and free server memory/slots after 5 minutes of inactivity (
IDLE_ROOM_TIMEOUT_MS).
Deployments to production servers use scripts/deploy.sh, which synchronizes files via rsync, preserves /srv/tron/data/, dynamically templates bitcycles.service or bitcycles-fnm.service depending on DEPLOY_USE_FNM, and triggers systemd reload:
# 1. Setup local environment configuration (optional):
cp .env.example .env
# Edit .env with DEPLOY_TARGET or multi-target profiles DEPLOY_TARGETS="primary,secondary"
# 2. Deploy to all configured targets (zero arguments loads all targets or default):
./scripts/deploy.sh
# Or deploy to a specific configured profile:
./scripts/deploy.sh primary
./scripts/deploy.sh secondary
# Or pass parameters on the fly:
./scripts/deploy.sh user@server /srv/tron 3000To allow automated deployments to restart the service and update systemd unit files without interactive password prompts, create /etc/sudoers.d/bitcycles-service on the target server:
<user> ALL=(ALL) NOPASSWD: /usr/bin/systemctl stop bitcycles.service
<user> ALL=(ALL) NOPASSWD: /usr/bin/systemctl start bitcycles.service
<user> ALL=(ALL) NOPASSWD: /usr/bin/systemctl status bitcycles.service
<user> ALL=(ALL) NOPASSWD: /usr/bin/systemctl enable bitcycles.service
<user> ALL=(ALL) NOPASSWD: /usr/bin/systemctl daemon-reload
<user> ALL=(ALL) NOPASSWD: /usr/bin/cp <path>/build/bitcycles.service.resolved /etc/systemd/system/bitcycles.service
<user> ALL=(ALL) NOPASSWD: /bin/cp <path>/build/bitcycles.service.resolved /etc/systemd/system/bitcycles.service
- Roadmap & Planned Features: See ROADMAP.md for the active milestone backlog (Game Room Lifecycles, Audio SFX, Single-player Bot, Mobile Layouts).
- Release History: See CHANGELOG.md for detailed version release notes.
This project is licensed under the terms of the MIT License.