Skip docs navigation
Public Examples
Tutorial
High-performance 2D rendering where you own the gameplay loop and state model.

Pixi.js Game Tutorial

Build Pixi Crowd Catch when you want a renderer-heavy 2D game that still leaves room lifecycle, player identity, and publishing to TPG.

What You Build

Pixi Crowd Catch has one animated host display and one controller surface:

  • the host display creates a Pixi Application through createHostApplication
  • controllers move between three lanes and tap Catch
  • controllers persist lane and score with api.setPlayerState
  • catch bursts travel as transient runtime messages with api.broadcast
  • the bundle is built with Vite and registered through the registry API

The tutorial below includes the complete host, controller, manifest, and registration source.

Create the Project

Start with the canonical TP Games project so the game inherits the tested scripts, CI workflow, validation, and bundled AI development skill:

If the author CLI is already installed, tpgames init pixi-crowd-catch replaces the first scaffold command.

This tutorial intentionally replaces the starter surface layout with the checked-in PixiJS example. Copy the example's root host.html and controller.html in place of host.html and controller.html, then remove vite.config.mjs before creating the vite.config.ts shown below. Do not keep both Vite config files.

Use the checked-in example as the reference layout:

Install the renderer-neutral game kit plus Pixi:

Configure the Author Tools

Add the device, bundle, and registry commands to package.json:

Configure vite.config.ts with the two browser surfaces and workbench:

Create tpgames.json:

Model Shared and Player State

Keep synchronized room facts focused. The example uses shared state for the wave and player state for each controller lane and catch count:

The controller owns its player state. The display treats it as render input, not as authority over gameplay.

Boot the Runtime

Both surfaces use bootIframeGame() and defineSimpleGame() from @tpgames/game-kit. The kit owns the default iframe postMessage bridge, infers shell-provided context from the URL hash when launched by TPG, and accepts fallback context for local previews.

Each iframe runs its own definition. The host uses repeatable surfacesReady notifications for idempotent initialization, while Pixi renders the confirmed state delivered through subscriptions after the shell echo.

On the host:

On the controller, keep controls simple and accessible:

Render with Pixi

The host display exports createHostApplication(mount, getSnapshot). It creates one Pixi Application, appends app.canvas, and reads the latest TPG snapshot from a focused store on each ticker frame.

That boundary matters:

  • Pixi owns high-frequency drawing.
  • TPG owns participant, lifecycle, shared-state, and player-state updates.
  • DOM updates are limited to status and score text.

Destroy the Pixi app when the page hides:

Send Controller Events

Use player state for durable controller-owned facts and runtime messages for transient effects:

The host subscribes with api.subscribeMessages and only accepts the pixi:caught payload schema before showing it.

Test on Multiple Devices Without Uploading

The example's @tpgames/sdk-dev-kit Vite plugin exposes the Pixi display and controller to the normal TPG room flow. Install cloudflared, then run:

The command starts Vite and a temporary Cloudflare Quick Tunnel, then opens the host room on https://play.tp.games. Scan the room QR with two or more phones to test real controller identity, transport, and input against your current source. No registry upload, API key, or bundle build is required.

To use a tunnel you already manage, keep its origin port aligned with the CLI:

A Quick Tunnel exposes the local Vite server and source modules publicly. Treat the generated host link as private because it contains the temporary development-manifest descriptor; share only the room QR or controller invite. Keep .env files, credentials, and private assets outside Vite's served root, and stop the command and tunnel when finished. Use --no-open when you only want the private host link printed.

Bundle and Register

Render the bundle:

For local interactive publishing, start the registry and browser approval shell from a TPG repository checkout in two terminals:

Then sign in and register from the Pixi example repo:

The registration script uploads the archive to /creator/uploads, submits the version, creator-publishes it with the selected credential, and fetches /games/{gameId}/manifest for verification.

Registration delivers a built game version; it does not create a developer account. Its default upload mode stops after upload, --mode submit requests review, and --mode publish --yes deliberately completes creator publication. Browser login stores a session only for the exact registry origin. Use TPG_API_KEY only for CI or another unattended environment, keep it in the environment rather than arguments or project configuration, and grant only the scopes required by the selected mode.

Launch Verification

After publishing, open the web shell, select the published Pixi game, join from a controller, and start the game. Verify:

  • the host iframe shows the animated Pixi stage
  • controller buttons and keyboard arrows move lanes
  • Catch updates the controller status and host score list
  • Settings and Lobby controls still route through the shell
  • refreshing the controller preserves the room seat and can continue play

Current Limitations

  • The example uses procedural graphics instead of a full asset pipeline.
  • Collision is intentionally teachable rather than physics-accurate.
  • Spectator surfaces are not included.
  • The registry review/publish path is API-driven for automation; creator and reviewer product workflows should still be tested separately for production submissions.

TPG repository contributors can mirror the source-example checks from the monorepo root: