Skip to main content

Get Started

This guide will help you create your first multiplayer game with Piggo. We’ll build a simple game that demonstrates the core concepts of the framework.
This quickstart assumes you’ve already completed the installation steps.

Create Your First Game

1

Define Your Game Builder

Every Piggo game starts with a GameBuilder that defines the game’s configuration:
import { GameBuilder, Background, Cursor } from "@piggo-gg/core"

type MyGameState = {
  score: number
}

type MyGameSettings = {
  maxPlayers: number
}

export const MyGame: GameBuilder<MyGameState, MyGameSettings> = {
  id: "mygame",
  init: (world) => ({
    id: "mygame",
    renderer: "pixi",  // or "three" for 3D
    netcode: "delay",  // or "rollback"
    settings: {
      maxPlayers: 4
    },
    state: {
      score: 0
    },
    systems: [
      // Systems will be added here
    ],
    entities: [
      Background({ rays: true }),
      Cursor()
    ]
  })
}
The GameBuilder returns a Game object with:
  • renderer: Choose “pixi” for 2D or “three” for 3D
  • netcode: “delay” or “rollback” for multiplayer sync
  • settings: Immutable game configuration
  • state: Networked game state that syncs across clients
  • systems: Behavior and logic processors
  • entities: Starting game objects
2

Add Systems for Game Logic

Systems contain the logic that runs every tick. Here’s an example from the Hoops game:
import {
  SystemBuilder, PhysicsSystem, SpawnSystem,
  PixiRenderSystem, PixiCameraSystem
} from "@piggo-gg/core"

export const MyGame: GameBuilder<MyGameState, MyGameSettings> = {
  id: "mygame",
  init: (world) => ({
    id: "mygame",
    renderer: "pixi",
    netcode: "delay",
    settings: { maxPlayers: 4 },
    state: { score: 0 },
    systems: [
      PhysicsSystem("local"),
      SpawnSystem({ spawner: MyCharacter, pos: { x: 0, y: 0, z: 0 } }),
      PixiRenderSystem,
      PixiCameraSystem({
        follow: () => ({ x: 0, y: 0, z: 0 }),
        resize: () => 1.0
      })
    ],
    entities: [
      Background({ rays: true }),
      Cursor()
    ]
  })
}
Common built-in systems:
  • PhysicsSystem - Rapier physics integration
  • SpawnSystem - Player spawn management
  • PixiRenderSystem / ThreeRenderSystem - Rendering
  • PixiCameraSystem / ThreeCameraSystem - Camera control
  • HUDSystem - UI overlays
3

Create the World

The World is the runtime that manages your game. Use DefaultWorld to get started:
import {
  DefaultWorld, PixiRenderer, ThreeRenderer
} from "@piggo-gg/core"

const world = DefaultWorld({
  pixi: PixiRenderer(),
  three: ThreeRenderer()
})
The DefaultWorld includes essential systems:
  • RandomSystem - Deterministic random numbers
  • ExpiresSystem - Auto-remove timed entities
  • ControlSystem - Input handling
  • InputSystem - Keyboard/mouse/touch
  • CommandSystem - Chat commands
  • NPCSystem - Non-player character logic
  • ActionSystem - Player actions
  • PositionSystem - Transform updates
4

Initialize Your Game

Put it all together in your entry point:
import { PiggoGG } from "./PiggoGG"
import { MyGame } from "./MyGame"

PiggoGG({ gameBuilder: MyGame })
This initializes the canvas, sets up audio, and starts the game loop.
5

Run the Development Server

Start the local dev server:
bun dev
This launches:
  • Web client at http://localhost:3000
  • Game server with WebSocket support
  • Hot reload for instant updates
Open your browser and start playing!

Full Example: Simple 2D Game

Here’s a complete example based on the Lobby game structure:
core/src/games/mygame/MyGame.ts
import {
  Background, Cursor, Entity, GameBuilder, HtmlChat,
  HtmlFpsText, HtmlLagText, Networked, NPC,
  PixiRenderSystem, World
} from "@piggo-gg/core"

type MyGameState = {
  playing: boolean
}

export const MyGame: GameBuilder<MyGameState> = {
  id: "mygame",
  init: () => ({
    id: "mygame",
    renderer: "pixi",
    netcode: "delay",
    settings: {},
    state: {
      playing: false
    },
    systems: [PixiRenderSystem],
    entities: [
      Background({ move: 0.5, rays: true }),
      GameController(),
      HtmlChat(),
      HtmlLagText(),
      HtmlFpsText(),
      Cursor()
    ]
  })
}

const GameController = (): Entity => {
  return Entity({
    id: "gameController",
    components: {
      networked: Networked(),
      npc: NPC({
        behavior: (entity, world) => {
          const state = world.state<MyGameState>()
          
          // Game logic runs here every tick
          if (world.tick % 60 === 0) {
            world.announce(`Tick: ${world.tick}`)
          }
        }
      })
    }
  })
}

Project Structure

Your game files should follow this structure:
core/src/games/mygame/
├── MyGame.ts          # Main game builder
├── MyGameEntities.ts  # Entity factories
├── MyGameSystems.ts   # Custom systems
└── MyGameConstants.ts # Game constants

Understanding the Game Loop

Piggo runs on a fixed tick rate (default: 20 ticks/second):
  1. onTick: Systems process game logic
  2. onRender: Visual updates (can run at 60 FPS)
  3. Network sync: State synchronizes across clients
// Systems run every tick
world.onTick({ isRollback: false })

// Rendering runs every frame
world.onRender()
The tick rate determines game simulation frequency, while rendering can run at higher frame rates for smooth visuals.

Next Steps

ECS Concepts

Deep dive into Entity Component System architecture

Creating Entities

Learn how to build game objects

Building Systems

Write custom game logic

Multiplayer Netcode

Implement rollback and delay netcode

Build docs developers (and LLMs) love