A monorepo of original browser games, built with TypeScript + Vite and deployed together to
a single GitHub Pages site. The repo uses npm workspaces: one root npm install, one root
lockfile, and shared hoisted dependencies across every game.
Not affiliated with any game studio. All names, characters, abilities, items, and lore in these projects are original works inspired only by generic genre archetypes.
Each game lives in its own workspace under packages/, described by a game.json next to its
package.json. That file is the single source of truth: the table below, the landing page, and
the deploy workflow are all generated from it.
| Package | Game | Genre | Stack | Summary |
|---|---|---|---|---|
packages/champs |
Arena Champions (아레나 챔피언스) | MOBA | Vite + React 18 + TypeScript + Phaser 3.80 | A three-lane 5v5 MOBA with champion select, turrets and inhibitors, minion waves, major neutral objectives, and a 2.5D vector battle view. |
packages/whiteout |
Frosthold: Last Ember (서리성채: 마지막 불씨) | Survival City-Builder | Vite + TypeScript + Phaser 3.80 | A frozen-survival city-builder: keep the central furnace burning, run idle resource buildings, and repel escalating cold-season sieges. |
packages/gridfall |
Gridfall (그리드폴) | Local-network Raycast Shooter | Vite + TypeScript + Phaser 3.80 | A two-player shooter rendered by raycasting a tile grid into vertical columns — no textures, no models, no 3D scene. Built on the same rollback core as Ringout so two people on one network can play with no server between them. |
packages/kingshot |
Kingdom Rise (킹덤 라이즈) | Strategy / Idle | Vite + TypeScript + Phaser 3.80 | A medieval kingdom builder with idle resource output, a town-center-gated building tree, troop training queues, and wave-based combat. |
packages/lastwar |
LAST SQUAD (라스트 스쿼드) | Squad Auto-Battler | Vite + TypeScript + Phaser 3.80 | A survival base-builder and squad auto-battler with a story campaign, endless horde defense, and a lane gate-runner mini-game. |
packages/ringout |
Ringout (링아웃) | Two-player Geometric Fighter | Vite + TypeScript + Phaser 3.80 | A two-body fighter drawn entirely from geometric parts, with no sprite sheets and no animation frames — every pose is computed. Built to be rolled back, so two people on the same network can fight with no server between them. |
packages/wirework |
Wirework (Wirework) | Top-down Ring Defense Action | Vite + TypeScript + Phaser 3.80 | Tether through a capacitor city and cut rear cooling nodes to stop autonomous siege machines. |
Every game ships with its own README.md, tests, and (where applicable) asset generators under
tools/. Some games add libraries of their own on top of the shared Vite + TypeScript +
Phaser 3 base - the Stack column above lists what each one actually uses.
The original single-game Arena Champions README now lives at
packages/champs/README.md.
open-games/
├── package.json # root: npm workspaces + fan-out scripts
├── package-lock.json # single lockfile for all workspaces
├── LICENSE # single Apache-2.0 license for the whole repo
├── CONTRIBUTING.md # repo-wide rules + new-game checklist
├── .gitignore # consolidated ignore rules
├── scripts/
│ ├── lib/games.mjs # loads + validates every packages/*/game.json
│ ├── gen-readme.mjs # regenerates the tables in this README
│ └── new-game.mjs # scaffolds packages/<slug> from the template
├── site/
│ ├── index.template.html # landing-page markup (no game data in it)
│ └── build.mjs # renders the landing page from every game.json
├── packages/
│ ├── _template/ # starter for a new game; not a workspace
│ └── <slug>/ # one workspace per game, each with a game.json
└── .github/workflows/deploy.yml # builds every workspace + landing page, deploys to Pages
Nothing outside packages/<slug>/ names an individual game or counts how many there are.
Requires Node 22 and npm 11.
Install everything with a single root install - npm workspaces hoist and share
dependencies across all games, and produce one root package-lock.json:
npm install # from the repo root; installs all workspacesDo not run npm install inside individual packages - the workspaces setup handles them.
Use npm's -w (workspace) flag to target a single package, where <slug> is any directory
under packages/:
npm run dev -w packages/<slug> # start the Vite dev server for one game
npm run build -w packages/<slug> # type-check + production build for one game
npm run test -w packages/<slug> # run one game's unit tests
npm run typecheck -w packages/<slug> # type-check only, no emitEach game builds with Vite and emits to packages/<slug>/dist.
The root package.json fans out across all workspaces via --workspaces --if-present:
npm run build # build every game, then generate the landing page
npm run build:games # build every game only
npm run build:site # generate the landing page into _site/ only
npm run test # run every game's tests
npm run typecheck # type-check every game
npm run docs:readme # regenerate the tables in this README from game.json
npm run docs:check # verify those tables are up to date (no writes)
npm run new-game # scaffold a new game; see below
npm run thumbs # re-capture the landing-page thumbnails (maintainer tool)Four steps, none of which touch a shared file:
- Scaffold it.
npm run new-game -- --slug foo --title "Foo Quest"copiespackages/_templatetopackages/fooand substitutes the placeholders. - Describe it. Fill in
packages/foo/game.json(genre, summaries, stack) and setstatustoplayablewhen it is ready to list. - Wire the install. Run
npm installfrom the root so the new workspace is linked. - Verify.
npm run typecheck && npm run test && npm run build, thennpm run docs:readmeto refresh the tables above. Runnpm run thumbsonce the game renders, so the landing page shows a real screenshot instead of a placeholder tile.
The landing page, the tables in this README, and .github/workflows/deploy.yml all pick the
game up on their own. See CONTRIBUTING.md for the full checklist and the
original-naming rules.
Every game deploys to a single GitHub Pages site at:
https://savagemanage.github.io/open-games/
The Pages workflow at .github/workflows/deploy.yml builds every workspace, collects each
packages/<slug>/dist into a <slug>/ subpath by globbing (no game is named in the workflow),
generates the root landing page, and publishes them together. Each subpath matches the base
configured in that game's vite.config.ts (production base /open-games/<slug>/, dev base
/):
| URL | Source |
|---|---|
https://savagemanage.github.io/open-games/ |
root landing page (site/index.template.html + site/build.mjs) |
https://savagemanage.github.io/open-games/champs/ |
packages/champs |
https://savagemanage.github.io/open-games/whiteout/ |
packages/whiteout |
https://savagemanage.github.io/open-games/gridfall/ |
packages/gridfall |
https://savagemanage.github.io/open-games/kingshot/ |
packages/kingshot |
https://savagemanage.github.io/open-games/lastwar/ |
packages/lastwar |
https://savagemanage.github.io/open-games/ringout/ |
packages/ringout |
https://savagemanage.github.io/open-games/wirework/ |
packages/wirework |
Enable Pages once under Settings → Pages → Build and deployment → Source: GitHub Actions.
See CONTRIBUTING.md for repo-wide rules: the new-game checklist, the
checks a change has to pass, and the original-naming policy. Individual games may add their own
guide on top - for example packages/wirework/CONTRIBUTING.md.
This project is licensed under the Apache License 2.0. A single LICENSE file at
the repo root applies to every workspace; each package's package.json also declares
"license": "Apache-2.0".
© 2026 Janghoon Lee