2D Game is the Phaser-based game template on Gipity. All files are fully editable - no locked template layer. Uses Phaser 3.80.1 via CDN with arcade physics.

When to use this: When the user asks for a 2D game - platformer, side-scroller, arcade, puzzle, endless runner, top-down, shooter, or RPG. For simple games (wordle, quiz, card games), use web-simple. For 3D multiplayer apps, use 3d-engine for a blank template or 3d-world for a playable rocket-launcher starter.

Quick Start - Start Here

STRONGLY RECOMMENDED: Begin every 2D game by adding the 2d-game template with add. It sets up Phaser 3, boot/game scenes, config, settings, and favicons for you. Only hand-roll files if the user explicitly tells you to skip the template.

add name=2d-game title="<Game Name>"

Starting over in an existing project: If src/ already exists and the user wants a clean rebuild, call file_delete on src first, then run add normally. Or pass force=true to add to overwrite in one step - destructive, so confirm with the user first. Unrelated content (media, data, notes) is preserved either way.

Naming: Use the user's name verbatim if given. If they didn't specify, blend "Gip" or "Gipity" into the name (e.g. "Gipity Racer", "Gip Tac Toe") - be creative but don't force it.

This creates a playable game immediately - colored rectangle player, keyboard controls, arcade physics, ground platform. Then edit scenes/game.js and settings.js to build your game.

Project Structure

src/
  index.html            - Phaser CDN, game container, module entry
  js/
    config.js           - Phaser.Game config, scene registration
    settings.js         - Tunable values (canvas, colors, physics, player, gameplay)
    strings.js          - User-facing display text
    scenes/
      boot.js           - Preloader with progress bar
      game.js           - Main game scene (gameplay logic)
  css/
    styles.css          - Page layout, canvas styling
  images/
    favicon-192.png
    favicon.ico

All files are editable. No read-only engine layer.

Phaser Essentials

Scene Lifecycle

Every scene has three key methods:

Loading Assets

In boot.js preload():

this.load.image('player', './images/player.png');
this.load.spritesheet('hero', './images/hero.png', { frameWidth: 32, frameHeight: 48 });
this.load.audio('jump', './audio/jump.mp3');

Creating Game Objects

// Sprite (from loaded image)
this.player = this.physics.add.sprite(400, 300, 'player');

// Rectangle (no image needed)
this.player = this.add.rectangle(400, 300, 32, 48, 0xf26522);
this.physics.add.existing(this.player);

// Static group (platforms)
this.platforms = this.physics.add.staticGroup();
this.platforms.create(400, 568, 'ground');

Physics (Arcade)

// Gravity is set in config.js via settings.physics.gravity
body.setVelocityX(speed);
body.setVelocityY(jumpForce);  // negative = up
body.setBounce(0.2);
body.setCollideWorldBounds(true);

// Collisions
this.physics.add.collider(player, platforms);
this.physics.add.overlap(player, coins, collectCoin, null, this);

Input

// Keyboard
this.cursors = this.input.keyboard.createCursorKeys();  // up, down, left, right
if (this.cursors.left.isDown) body.setVelocityX(-speed);

// Specific keys
this.spaceKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);

// Touch/pointer
this.input.on('pointerdown', (pointer) => { ... });

Text

this.add.text(400, 30, 'Score: 0', {
  fontSize: '24px', color: '#ffffff', fontStyle: 'bold',
}).setOrigin(0.5);

Camera

this.cameras.main.startFollow(this.player, true, 0.1, 0.1);
this.cameras.main.setBounds(0, 0, worldWidth, worldHeight);

Common Phaser 3 Pitfalls

These are the failure modes that most often turn the whole screen black. Read before writing a game scene.

Build Incrementally

For anything non-trivial, resist the urge to write the whole game in one Write call. Work in small, verified steps:

  1. Add the template (gipity add 2d-game) and deploy - confirm the starter game renders.
  2. Customize ONE element (e.g. replace the player rectangle with your sprite). Deploy, screenshot, confirm it renders.
  3. Add the next element (ground, enemies, collectibles) one at a time, deploying and verifying between each.
  4. Only after the core loop works, layer on polish (parallax, particles, HUD, touch controls).

A 500-line rewrite of scenes/game.js is very hard to debug when something breaks - a single bad API call turns the whole screen black with no useful error. Small steps keep the failure surface tiny.

Verification After Deploy

After every gipity deploy dev:

Animations

this.anims.create({
  key: 'walk',
  frames: this.anims.generateFrameNumbers('hero', { start: 0, end: 3 }),
  frameRate: 10,
  repeat: -1,
});
this.player.anims.play('walk', true);

Adding a New Scene

  1. Create src/js/scenes/myScene.js:
export class MyScene extends Phaser.Scene {
  constructor() { super('MyScene'); }
  create() { /* ... */ }
  update() { /* ... */ }
}
  1. Register in config.js:
import { MyScene } from './scenes/myScene.js';
// Add to scene array: scene: [Boot, Game, MyScene],
  1. Switch scenes: this.scene.start('MyScene');

Genre Recipes

Side-Scroller / Platformer

Top-Down (Zelda-style)

Endless Runner

Arcade / Shooter

Puzzle

Mobile / Touch

Phaser handles touch automatically for pointer events. For virtual controls:

// On-screen buttons
const jumpBtn = this.add.rectangle(700, 500, 80, 80, 0x333333, 0.5).setInteractive();
jumpBtn.on('pointerdown', () => { body.setVelocityY(jumpForce); });

For mobile-responsive canvas, the template uses Phaser.Scale.FIT + CENTER_BOTH by default.

Deploy Verification

Use the browser tool to verify deploys when it matters - first deploy, structural changes (new pages, new frameworks, changed imports), or when something might have broken. Skip verification for trivial changes (copy tweaks, style adjustments, config values).

To verify: browser action=open url=<deployed-url> - waits for async modules, captures console errors automatically. Check output for [Console errors captured after page load]. Use browser action=screenshot to confirm visual correctness.

Debugging in production: Add console.error() calls to app code for diagnostics, redeploy, then use browser action=console to read the output. Remove debug logging when done.