Back to projects
2026Live

Arcadium

20 games. No engine. Just React, Canvas, and raw JavaScript.

ReactCanvas 2DVibration APIWeb Audio APIReact Router
View Live
Arcadium screenshot

No Game Engine. Just the Browser.

The constraint came first: no Phaser.js, no Unity WebGL, nothing that abstracts away what the browser is actually doing. I wanted to understand how games work at the level of state machines, collision geometry, and frame-rate-independent physics — not how to configure a game engine. So I started with Snake on a Canvas element and a requestAnimationFrame loop, and kept going from there.

The landing page was designed to put you in the right headspace before you pick a game. A boot-style loader with CRT scanlines and terminal text, a particle field ticking away in the background, game cards that tilt toward the cursor. It's supposed to feel like powering on something old.

Each Game Lives in Its Own World

Twenty games, each a completely self-contained React component with its own game loop and state. Nine of them — Snake, Tetris, Asteroids, Pong, Flappy Bird, Breakout, Space Invaders, Aim Trainer, Dodge — run on Canvas 2D. The others, things like 2048, Boggle, Hangman, Simon Says, Memory Match, are DOM-based with CSS animations handling the visual work.

Two systems run across all of them. A useHaptics hook that calls the Vibration API on phones and synthesizes Web Audio tones on desktop, so collisions and game-overs have some physical weight. And a floating Joystick component that appears over canvas games on touch screens, translating swipes into movement vectors the game logic can actually use.

Launcher → Route → Game

React Router v6 handles the structure. Every game is at its own path under /arcadium/*. The landing page is the hub — a filterable grid of game cards by category (SOLO, PVP·AI, PUZZLE, BRAIN, ARCADE, REFLEX, SKILL) with a neon scanline look from a shared games.css.

01
ArcadeLoader

Boot sequence with CRT scanlines, glitch text, and terminal-style loading lines before the launcher appears

02
Game Hub

GameCard grid with category filters, hover tilt, and a HowToPlay modal per game. ParticleField canvas runs live in the background.

03
Canvas Games

Snake, Tetris, Asteroids, Pong, Flappy Bird, Breakout, Space Invaders, Aim Trainer, Dodge — Canvas 2D with requestAnimationFrame loops and particle effects on events

04
DOM Games

2048, Boggle, Memory Match, Typing Speed, Reaction Time, Hangman, Simon Says, Whack-a-Mole, Odd One Out, Number Sequence — React components with CSS animations

05
Mobile Layer

Floating Joystick overlaid on canvas games for touch devices; useHaptics fires vibration or Web Audio on score events, collisions, and game-over

Under the Hood

Canvas 2D + requestAnimationFrame

Nine games render directly to canvas with delta-time physics. Snake has trail particles and bonus food timers, Asteroids splits rocks on hit, Breakout uses AABB collision with per-side bounce, Dodge spawns projectiles on a curve.

React Router v6

Each game is its own route under /arcadium/*. GamesApp wraps the router, GamesLanding is the hub. Lazy loading keeps the initial bundle small.

useHaptics Hook

Calls navigator.vibrate() on mobile, synthesizes short Web Audio tones on desktop. Wired into every meaningful game event — hit, score, game-over.

Joystick Component

Touch joystick rendered as a DOM overlay on canvas games. Outputs directional vectors, with discrete snapping for grid-based games (Snake, Tetris) and smooth analog for Pong and Asteroids.

What Made It Hard

  • Snake's golden bonus food has its own countdown that has to run independently of the main game loop. Managing that with useRef was the only way to avoid stale closure bugs inside requestAnimationFrame callbacks.
  • Asteroids wraps the playfield — ship goes off the right edge and reappears on the left. Getting that to work correctly for every entity (ship, bullets, asteroids) simultaneously without special-casing anything took more iterations than expected.
  • The Joystick needed to work differently for different games. Grid-based games like Snake need snapped cardinal directions. Pong and Asteroids need smooth analog vectors. Same component, two output modes, determined by the game that mounts it.
  • Hangman draws the figure incrementally as wrong guesses accumulate. Each of the ten strokes is a separate SVG element with a CSS animation triggered by a calculated delay based on guess count.