Skip to content

Repository files navigation

paper-io-clone

Browser game inspired by Paper.io: claim territory by enclosing areas with your trail, avoid getting your trail cut by others, and compete against AI bots on a large grid. Single-player / local only — no server or multiplayer.

Stack: React 18, TypeScript, Vite 5, Canvas 2D.


Quick start

  • Prerequisites: Node.js 18+ (LTS recommended), npm.
  • Install: npm install
  • Dev (HMR): npm run dev — open the URL Vite prints (usually http://localhost:5173).
  • Production build: npm run build — outputs to dist/ (tsc -b then vite build).
  • Preview build locally: npm run preview — serves dist/ (default http://localhost:4173).

How to play

  • Menu: name + skin, then start.
  • Controls: arrow keys (or WASD) to turn; you move continuously.
  • Goal: expand your colored territory; kills when you cut another player’s trail; you die if someone cuts yours or you hit a border (red edge).
  • HUD: map coverage %, kills, survival time; leaderboard lists alive players by territory %.

Project layout

Path Role
src/main.tsx, src/App.tsx App bootstrap; root renders GameCanvas.
src/components/ UI overlays: GameCanvas, StartScreen, HUD, Leaderboard, DeathScreen, SkinSelector.
src/hooks/useGameState.ts Creates Game, subscribes to state for React, exposes startGame / restartMenu.
src/game/ Core simulation: grid, players, bots, territory fill, collisions, camera, renderer, input.
src/styles/ Global / game layout CSS.

Game engine (conceptual)

  • Game owns the loop: fixed 20 Hz simulation (fixedDt), requestAnimationFrame for draw; React state updates are throttled ~10/s to avoid re-rendering every frame.
  • Grid — cell ownership (territory, trails, empty); map is 200×200 cells, 20px per cell (constants.ts).
  • Player / BotController — movement and bot AI.
  • TerritorySystem — flood-fill / closure when a trail completes a loop back to own territory.
  • CollisionSystem — trail cuts, border deaths.
  • Renderer — all Canvas drawing; Camera follows the human player with smoothing.
flowchart LR
  subgraph react [React]
    GameCanvas --> useGameState
    useGameState -->|subscribe| Game
  end
  subgraph loop [Game loop]
    Game --> Input
    Game --> Grid
    Game --> TerritorySystem
    Game --> CollisionSystem
    Game --> Camera
    Game --> Renderer
    Game --> BotController
  end
  Renderer --> Canvas[(canvas)]
Loading

Data the UI cares about

GameState (src/game/types.ts) is what subscribe pushes to React:

  • phase: menu | playing | dead
  • humanPlayer: position, direction, trail, skin, kills, territory %, alive
  • leaderboard: sorted alive players by territory %
  • mapCoverage: % of map that is claimed (any player)
  • timeSurvived: seconds since run started

Tweaking behavior

Most knobs live in src/game/constants.ts: grid size, cell size, player speed, BOT_COUNT, bot respawn delay, camera lerp, colors/skins, tick rate.


Scripts

Command What it does
npm run dev Vite dev server + HMR
npm run build Typecheck + production bundle → dist/
npm run preview Static serve of dist/
npm run lint ESLint on the repo

Debugging tips

  • Black / wrong canvas size: Game listens to window.resize and resizes the renderer; check the canvas element in DevTools if layout CSS changed.
  • “React not updating”: intentional — game state to React is throttled; use gameRef in useGameState if you need to inspect Game from a temporary useEffect while developing.
  • Logic bugs: follow Game.ts tick order and TerritorySystem / CollisionSystem; grid semantics use EMPTY_CELL and TRAIL_CELL_OFFSET in constants.ts.
  • Type errors: npm run build runs tsc -b; fix there before relying on the editor only.
  • Lint: npm run lint — config is eslint.config.js (flat config).

About

Browser-based Paper.io clone — claim territory by enclosing areas with your trail while avoiding AI bots. Built with TypeScript using Canvas 2D rendering. Single-player, no server required.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages