Skip to content

2D · TypeScript · Free download

Phaser game starter

A complete 2D game engine with scenes, input, animation, and built-in physics options.

Build locally with your preferred editor or AI coding agent. You keep the source and choose where to publish.

No account needed. Extract into a new folder to keep your existing projects safe.

Phaser starter illustration

What you can build

Sprite-based browser games. The starter includes a minimal movement and collection scene so you can verify the runtime before adding your own rules, artwork and levels.

Included in this kit

  • Phaser for 2D rendering
  • Plain movement without a separate physics integration
  • HTML/CSS interface, keyboard input and editable game logic
  • Vite development server, TypeScript, linting and Vitest checks

Browser games are the supported target. Native desktop and mobile packaging are not included.

Run your game

Requires Node.js >=24.0.0 and pnpm@12.10.1.

  1. Download and extract the kit into a new folder.
  2. Open that folder in your editor and run pnpm install.
  3. Run pnpm dev and open the address shown in your terminal.

The local HTTPS setup may ask you to trust its development certificate. See the included README for setup and troubleshooting.

Run pnpm build to prepare a release. Explore hosting when you are ready to share it.

Preview the generated project

These 32 files and commands come from the same configuration used by the download.

Project files
  • dev-domains.config.mjs
  • scripts/local-environment.mjs
  • scripts/local-environment.d.mts
  • .gitignore
  • .npmrc
  • pnpm-workspace.yaml
  • tsconfig.json
  • .oxlintrc.json
  • .oxfmtrc.json
  • src/score.ts
  • src/input.ts
  • vite.config.ts
  • spawnkit/config.ts
  • spawnkit/vite.ts
  • src/style.css
  • src/main.ts
  • src/simulation.ts
  • vitest.config.ts
  • src/score.test.ts
  • src/simulation.test.ts
  • src/ui.ts
  • src/input.test.ts
  • src/main.test.ts
  • src/ui.test.ts
  • package.json
  • game.config.json
  • spawnkit.config.ts
  • public/spawnkit-listing.schema.json
  • public/media/cover.svg
  • index.html
  • README.md
  • .spawn/state.json
Available commands
pnpm dev
dev-domains run app
pnpm build
tsc --noEmit && vite build
pnpm preview
vite preview
pnpm lint
oxlint --no-ignore src vite.config.ts
pnpm test
vitest run --coverage
Entry point: src/main.ts
import { createUI } from "./ui"
import Phaser from "phaser"
import "./style.css"
import { advanceScore } from "./score"
import { createSimulation } from "./simulation"
import { createInput } from "./input"

class GameScene extends Phaser.Scene {
  private player!: Phaser.GameObjects.Rectangle
  private collectible!: Phaser.GameObjects.Arc
  private controls!: ReturnType<typeof createInput>
  private simulation = createSimulation({ x: 400, y: 280, speed: 250, minX: 14, maxX: 786, minY: 14, maxY: 546 })
  private score = 0
  private ui = createUI()
  create() {
    this.add.grid(400, 280, 800, 560, 40, 40, 0x101e29, 1, 0x213844, 0.7)
    this.player = this.add.rectangle(400, 280, 28, 28, 0xb5f46a)
    this.collectible = this.add.circle(580, 280, 11, 0xffc77a)
    this.game.canvas.tabIndex = 0
    this.game.canvas.setAttribute("aria-label", "Game. Use arrow keys to collect gold.")
    this.controls = createInput(this.game.canvas)
    this.events.once("shutdown", () => this.ui.dispose())
  }
  update(_time: number, delta: number) {
    if (!this.controls || this.ui.isPaused()) return
    const position = this.simulation.step(this.controls.read(), delta / 1000)
    this.player.setPosition(position.x, position.y)
    if (Phaser.Math.Distance.Between(this.player.x, this.player.y, this.collectible.x, this.collectible.y) < 28) {
      this.score = advanceScore(this.score)
      const label = document.querySelector("#score")
      if (label) label.textContent = "Collected: " + this.score
      this.collectible.setPosition(Phaser.Math.Between(35, 765), Phaser.Math.Between(70, 525))
    }
  }
}
const game = new Phaser.Game({ type: Phaser.AUTO, parent: "game", width: 800, height: 560, backgroundColor: "#101e29", input: { keyboard: false }, scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH }, scene: GameScene })

window.addEventListener("pagehide", () => game.destroy(true), { once: true })

Related kits