p5.js Game Tutorial
Build p5 Signal Garden from an empty Vite project when you want a creative-coding sketch to become the host display while TPG owns room lifecycle, player identity, state sync, and publishing.
What You Build
p5 Signal Garden has one animated host display and one accessible controller surface:
- the host display mounts a p5 sketch with
new p5 - TPG shared state owns the current prompt and round
- TPG player state owns each controller signal and note
- the p5
draw()loop receives a read-only snapshot of TPG state - 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 p5-signal-garden
replaces the first scaffold command.
This tutorial intentionally replaces the starter surface layout with the checked-in
p5.js 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.js shown below. Do not keep both Vite config files.
Use the checked-in example as the reference layout:
Install the renderer-neutral TPG game kit plus p5:
Configure the Author Tools
Add the device, bundle, and registry commands to package.json:
Configure vite.config.js with the two browser surfaces and workbench:
Create tpgames.json:
Model Creative State
Keep p5 as a renderer. The synchronized game facts stay in TPG state so every display, controller, reconnect, and future runtime mode sees the same room snapshot.
The example uses shared state for the round prompt:
Each controller writes its own player state:
The host reads those player states when drawing. It does not let p5 mutate authoritative room state.
Boot the TPG Runtime
Both surfaces use bootIframeGame() and defineSimpleGame() from @tpgames/game-kit. The kit owns the default iframe postMessage bridge setup, infers the shell/referrer origins, and then boots the game runtime with the surface context. The host initializes shared state when all required surfaces are ready or the game has started:
Each iframe runs its own definition. Surface readiness is separate from
lifecycle: surfacesReady can repeat, initialization must be idempotent, and
the sketch should consume the confirmed shared-state subscription echo.
The controller only needs its current participant, shared prompt, and own player state. Buttons stay in DOM instead of inside the sketch so labels, focus, and screen-reader status remain accessible.
Mount the Sketch
Create the p5 sketch after the host surface DOM exists:
The sketchSnapshot variable is replaced when TPG subscriptions fire. That keeps the p5 loop simple: every frame reads the latest immutable snapshot and draws it.
Handle Resize and Cleanup
p5 will keep animation loops and event handlers alive until removed. Always clean up before replacing the sketch or unmounting the host iframe:
Use CSS to make the sketch container fill the host panel, then call p.createCanvas(container.clientWidth, container.clientHeight) and p.resizeCanvas(...) from p.windowResized. Avoid hard-coding canvas pixels into gameplay state.
Send Controller Signals
The controller uses accessible DOM buttons to choose a signal and a text input for a short note. On submit, it writes player state and emits an analytics milestone:
Use player state for durable facts that should survive reconnect. Use transient runtime messages only for effects that do not need to be reconstructed later.
Test on Multiple Devices Without Uploading
The example's @tpgames/sdk-dev-kit Vite plugin exposes the p5 display and DOM
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 p5 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 p5 Signal Garden, join from a controller, and start the game. Verify:
- the host iframe shows the animated signal garden canvas
- controller signal buttons are keyboard focusable and update
aria-pressed - Send signal updates the controller status and host signal list
- New prompt updates shared state for all connected surfaces
- Settings and Lobby controls still route through the shell
- refreshing the controller preserves the player seat and can continue play
Current Limitations
- The example uses procedural drawing instead of loading art assets.
- The host sketch is display-only; controller UI remains DOM-first for accessibility.
- Spectator surfaces are not included.
- Registry review and publish are API-driven for automation, so product creator/reviewer flows should still be tested separately for production submissions.