Set once. Inherited by every game. A game overrides something here only with a deliberate reason written into its SPEC.
src/
main.ts # boots the Phaser.Game with the config below
scenes/
Boot.ts # set scale/input, then -> Preload
Preload.ts # load nothing for primitive games; -> Game
Game.ts # the ONE mechanic lives here. This is the only file you reopen.
GameOver.ts # score + high score + restart
feel/ # reusable game-feel (built during game 0, never rewritten)
shake.ts burst.ts hitstop.ts sfx.ts
lib/
input.ts # onAction() single-input abstraction
score.ts # score + persistent high score
palette.ts # the studio palette (below)
public/ # only if a SPEC names real assets (Kenney CC0)
Design resolution 1920×1080 (16:9), Phaser.AUTO, Scale.FIT, centered. The
canvas renders at 1080p so text/shapes/images stay crisp on modern (incl. retina/4K)
displays instead of being upscaled from a low buffer. Author element sizes (fonts,
rects, fall speeds) in this 1920×1080 space; positions use scale.width/height so they
scale automatically.
const config: Phaser.Types.Core.GameConfig = {
type: Phaser.AUTO,
width: 1920, height: 1080,
backgroundColor: PALETTE.bg,
roundPixels: true,
scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH },
scene: [Boot, Preload, Game, GameOver],
// pixelArt: true ONLY when a SPEC introduces pixel sprites (games 3+).
};A fixed 6-colour set. Recolour = pick a different accent, never a new file.
export const PALETTE = {
bg: 0x14131a, // near-black base
ink: 0xe8e6e3, // text / UI
hot: 0xff5d5d, // danger / player accent
cool: 0x4ec9b0, // safe / target accent
warn: 0xffd166, // highlight / score pop
mute: 0x6b6a78, // inactive / hazard fill
} as const;One display font only: system ui-monospace for games 0–2 (zero assets). Bundle a
single CC0 pixel font from game 3 if you want, and commit it to the template once.
- Rects use a 6px corner radius; hazards use 0 (sharp = dangerous). One outline
weight (2px
ink@ 35% alpha) or none — consistent within a game. - Colour = meaning, every game:
hotkills/costs,coolis wanted/safe,warnis feedback/score,muteis neutral. Never reassign per game. - One easing family: Quadratic/Cubic, never linear for anything the player watches. Spawns scale in, deaths scale out.
- Full studio bible (and the pixel-era pipeline, which activates at game 3):
ART.md.
One entry point. Works on pointer, touch, and keyboard so every game is mobile-safe.
// onAction(scene, cb) -> fires cb() on pointerdown / touch / Space / Enter
// onAxisX(scene) -> returns a 0..width target x (pointer/finger x), for dodgers/aimersGames that need only "go" use onAction. Games that need horizontal aim use onAxisX.
Nothing more elaborate than these two ships in the template.
- localStorage key:
quench:<game-id>:hi(e.g.quench:00-dodger:hi). Score.start(),Score.value,Score.commit()(writes high score if beaten).
shake(scene, ms, intensity) // camera shake
burst(scene, x, y, color, n) // one-shot particle pop
hitstop(scene, ms) // freeze-frame on impact
sfx.play('hit' | 'pickup' | 'start') // WebAudio blips, no asset filesJuice is applied from here, never hand-rolled per game. This file is the entire deliverable of the old "Juice Lab" idea — it is a module, not a game.
- Everything positions relative to
scale.width/height, never hardcoded screen px. roundPixels: true. Pause the scene on blur so a tabbed-away game doesn't ramp difficulty in the background.