gameplateState. Loop. Input. Scenes. Selectors. Ship a game today.
npm install gameplate
import { createGame, defineActions } from 'gameplate';
const actions = defineActions<{ x: number }>()({
moveBy: (s, dx: number) => ({ x: s.x + dx }),
});
const game = createGame({
state: { x: 0 },
actions,
update: (state, dt, actions) => {
if (game.keyboard.isDown('ArrowRight')) actions.moveBy(200 * dt);
},
render: (state) => draw(state), // 👈 your renderer, any tech
});
game.start();
That's the whole API surface. Read it, write it, ship it.
strict: true + every noUnchecked* flag. Inference does the work — no as any, ever.menu → start → playing with compile-time checks. Send a wrong event? TypeScript stops you.publint clean. Provenance signed. Tree-shakeable.type State = { player: { x: number; y: number }; score: number };
const actions = defineActions<State>()({
move: (s, dx: number, dy: number) => ({
...s,
player: { x: s.player.x + dx, y: s.player.y + dy },
}),
score: (s, pts: number) => ({ ...s, score: s.score + pts }),
});
const game = createGame({ state: { player: { x: 0, y: 0 }, score: 0 }, actions });
game.actions.move(10, 0); // ✅ typed
game.actions.move('lol', 0); // ❌ TS error — caught at compile time
That's state + actions. Add update for input handling, render for drawing, and you have a game.
createGame |
One-call setup — state + loop + input wired together. |
createStore |
The underlying typed store (subscribe, getState, setState). |
defineActions |
Curried generic that gives perfect IntelliSense without retyping S. |
createLoop |
Bare loop — variable or fixed-step with interpolation alpha. |
createKeyboard / createPointer / createGamepad |
Normalized input — keys, pointer, and Standard Gamepad with edge detection. No-op stubs on the server. |
createMachine |
Compile-time-checked finite state machine for scenes/menus. |
createSelector |
Reselect-style memoization, ~30 LOC. |
createRecorder / replay |
Deterministic record + replay of every action. JSON-serialisable. |
gameplate doesn't ship a renderer — it ships the glue. Drop into:
Switch renderers without changing one line of game logic. Patterns for each →
⌨ keyboard ─┐
🖱 pointer ─┼──▶ actions ──▶ store ──▶ selectors ──▶ render ──▶ 🎨 your renderer
🛰 network ─┘ │ ▲
▼ │
scene FSM ──────────────────┘
loop (dt, alpha) ──▶ update ──▶ actions
A handful of composable functions. Pick the ones you need. Ignore the rest.
publint + @arethetypeswrong/cli clean on every PRPRs welcome. See CONTRIBUTING.md. Architecture decisions are recorded in DECISIONS.md.
git clone https://github.com/yankouskia/gameplate
cd gameplate
pnpm install
pnpm test:watch
MIT © Alex Yankouski · Sponsor →
If gameplate saved you a weekend, ⭐ star it — it's the easiest way to say thanks.