Multiplayer
Everything below is only in games flagged multiplayer.
TipTap.net is a separate module the platform injects only into games built as multiplayer games. In every other game the namespace is not there at all, and calling into it is a TypeError on a line you will not see fail — so a single-player game must not carry a TipTap.netcall “just in case”. Nothing here changes the sandbox: connect-src is still 'none' and your game still cannot open a socket. The connection lives outside the frame and the platform holds it.
Is it there? Ask before anything else
Two different questions, and a multiplayer game needs both answered before it does anything: whether this game was built as a multiplayer game, and whether a platform is around the frame right now. The second is false every time you open the file yourself, which is most of how you will build it.
Rooms and sessions
A public match against strangers, a private room behind a code, or a rejoin after the player's phone locked. Each returns a promise that rejects, and each also takes a callback if that suits your code better.
The room, the lobby, the players in it
Replicated state — the primitive most games actually want
Most multiplayer games are shared state rather than message pipes: a board, a deck, a canvas, a puzzle, the current question. Reach for this before you reach for messaging.
Only the host writes. Everyone else asks.
If you have built multiplayer with Playroom, Colyseus or anything peer-to-peer, this is the assumption to drop first. One client in the room is the host. state.set and its siblings called on any other client warn on the console, return falseand change nothing — there is no exception, no partial write and nothing queued for later. A non-host player's move is a request; the host decides and writes the result. Structure the game that way from the beginning, because the version built on the other assumption has three players in four who simply cannot act.
Turn order
A second conditional module on top of the first — it arrives only in a game that declares itself turn-based, so in another multiplayer game TipTap.net exists and TipTap.net.turn does not.
Messaging
Time, and randomness that agrees
The two things every multiplayer game gets wrong on its first attempt. Date.now() disagrees between real devices by seconds, and one Math.random() in a path two clients both run is a desync with no error attached to it. Nothing in the upload path checks for either — there is no lint and no warning — so a game that gets them wrong validates clean and breaks only once somebody else joins.
Authority
One client is the host and decides; everyone else reads the decision. The host can change mid-match — somebody's phone locks — and the next host resumes from state the platform kept, not from anything you saved.
Resilience — handled, not yours
Phones lock, tabs go to the background, Wi-Fi hands off to cellular. On a feed of games played on phones that is most sessions, and it is the SDK's problem rather than yours.
Friends and invites
Only in a game that declares private rooms. It is how somebody fills a room with people they already know instead of with strangers — and it is a picker, not a permission.
Quick chat, pings and the chat panel
Only in a game that declares comms. This is the whole of what two players in a public room can say to each other, and it is deliberately narrow: a fixed list of phrases the platform writes, and a marker dropped on a position. Your game re-renders them and never authors one — which is exactly what lets a public room refuse free text without leaving strangers with no way to interact at all.
What is not there yet
Written down rather than left to be discovered. Some of these names exist on the object and do nothing, which is friendlier than a crash and is still not a feature you can build on.
Testing it
Two browser windows on the Playground is the smallest real test, and the platform's validator is not one — it runs a single client, so it can tell you the file loads and can tell you nothing at all about whether the match works.
Real-time simulation
There are two ways to get a simulation on this platform, and today only one of them is reachable. The choice is confined to this section: everything above it is TipTap.net, and rooms, replicated state, turn order, messaging and chat behave identically either way.
| tier | what you write | where it stands |
|---|---|---|
| Modules | A fixed menu of four platform-authored simulations. You pick one and configure it; the physics, the tick and the netcode are ours. | What you use today. Declarable in Creator Studio, refereed by the relay, and the tier every simulation game here runs on. |
| Creator logic | A simulation you write. One TypeScript file, compiled server-side to WebAssembly and checked for determinism rather than trusted to be deterministic. | In development. It compiles, gates and runs, and it is not reachable from the Creator Studio — no route loads it, and it has not yet run in a browser. |
Which one should you build on right now? Modules. Not because they are the better design — the whole point of the other tier is that a fixed menu of four genres is a ceiling — but because you cannot publish a game on creator logic today, and you can publish one on a module. If what you want is on the menu below, take it. If it is not, the honest answer is that this platform cannot host your game yet, and the tier that will is not ready to be waited on with a deadline.
Nothing below is deprecated. The four modules are shipped, supported, and pinned bit-for-bit by a determinism test because the games running on them have to keep producing the same match tomorrow that they produced today. There is no removal date and no migration being asked of you. Creator logic → is worth reading now if you are choosing what to build next; it is not worth blocking on.
Four modules ship, and a game declares exactly one. arena2d — top-down 60 Hz, circular bodies, walls, projectiles, resources and respawns, for arenas and shooters. turnphysics — one actor at a time simulated to rest, for pool, golf and artillery; no prediction is involved at all, which makes it the most forgiving of a bad connection. platformer2d — tilemaps, gravity, one-ways, coyote time, wall jump, and kinematic moving platforms a rider inherits correctly inside prediction. racer — vehicle bodies, tire model, spline tracks, checkpoints and laps, with correction smoothing tuned so a contested overtake does not spin the car, and the only module that will drive its own empty seats.
Turn it on in Creator Studio under Advanced → Multiplayer SDK, or with PUT /api/games/[id]/multiplayer, or multiplayer_modules on update_game_metadata. Declaring an unknown or unbuilt name is refused by name and tells you what you may declare instead.
There is no 3D. arena3d is named in the spec as the flagship first-person module and it is not built; declaring it is refused. Neither physics (general rigid bodies — stacking, joints) nor fighter exists either. If you were asked for a 3D shooter, you cannot build one here — say so rather than shipping a 2D game that pretends. Creator logic is where that eventually changes, because a genre stops being a platform release once you write the simulation yourself; that is a reason to expect the ceiling to move, and not a date.
Two more limits worth knowing before you design around them. Play again currently reloads the game document — a simulation session cannot span two matches. And sim.query resolves on the host and travels, so it is authoritative but it is not free; poll it at a rate you have measured, and pass { timeoutMs } rather than relying on the five-second default.
A room’s seats are its ceiling, not its attendance. Every module lays out one body per seat, so two people matched into an eight-seat room get eight bodies and nobody is driving six of them. In a racing game that is a row of parked cars on the grid, and it is the first thing a player hits.
racer can fill them: config.botSeats is a bitmask of the seats the module drives with its own autopilot, and config.botStartTick holds them until your countdown is over. Build the mask from net.peerAtSeat(i) === null immediately before sim.define, because it has to be part of the config — every client simulates every car, so a bot’s input must be something all of them derive rather than something one of them sends. The other three modules have no autopilot, so there this is a design decision you have to make deliberately rather than one to discover on the grid.
A dev session never gets a simulation. A room opened against a dev-session grant is served at relay tier deliberately, whatever the game declares — so TipTap.sim is injected, every call queues, and each one settles as simulation unavailable about fifteen seconds later. The same is true of a file you open yourself. That is the expected result rather than a broken setup, and it is worth knowing before you spend an evening on the wrong theory.
What follows is the shape the tier has. Nothing in the game document simulates anything — the simulation is WebAssembly in a Worker the platform owns, and the game supplies geometry once and intent every frame. Design it gently or the netcode gets blamed for the design: slow time-to-kill, generous hitboxes, a small map, short rounds.

