Command line
The spawnite command comes from @spawnite/cli, which a game that spawnite create writes carries as a dev dependency. spawnite help prints every command. This page adds what the help does not say, grouped by what a command does; the help alone covers a command this page leaves out. A coding agent reaches the same commands as MCP tools.
Every command
Section titled “Every command”Every command except completion takes --json, which prints one object with "schemaVersion": 1 in place of the lines a person reads. A command that runs long, model make, model check, play profile and play join, prints each phase to stderr as it starts, such as Running models/imp.py in Blender, so stdout keeps the reply alone. The MCP’s tools send the same lines as progress notifications. Every command also takes --ports <first-last> and --room-ports <first-last>. --ports keeps each server it starts, the dev server, the build’s preview and the room, to the first free port of the range that no other server it starts was given. --room-ports keeps the room to a range of its own. GAME_PORTS and GAME_ROOM_PORTS set the same. Without them, the system picks each port. A refusal prints its message alone to stderr, or { "schemaVersion": 1, "error": { "type", "message" } } to stdout with --json. The exit code is 0 on success, 1 on a refusal, and 2 for a command line the cli does not accept.
The dashboard
Section titled “The dashboard”pnpm spawnite with no command, in a terminal, opens a dashboard built with Ink. Its Games panel lists the games in the folder, one row each, pinned games first with a ★, and its Other panel under it lists the screens that are not a game’s: New game and Account. Beside them, in a terminal 80 columns wide or wider, a panel says what the selected row holds: for a game, whether a playtest runs, at which address and whether your agent’s MCP server started it, how many replay sessions it keeps, and its folder.
↑↓ or the wheel move through both panels, tab jumps between them, ⏎ or a second click opens the row, p pins or unpins the selected game, esc goes back, a click on a crumb in the top bar goes back to it, and q quits. The pins are kept per machine in pinned-games.json, beside the login. A folder that is itself a game opens on that game.
A game’s Playtest tab has a Play button: with no playtest running, it starts one as spawnite play start does and opens it in your browser, and with one running, whoever started it, it opens that one. While one runs, the tab shows the agent’s session: the copy of the game its headless browser holds paused until the agent steps it, with who started it and so what stops it. The tab also shows a Stop playtest button for one spawnite play start or the dashboard began, its devtools tree in a World panel and its newest console lines in a Console panel. o opens and x stops. ← and → or a click move between a game’s tabs.
Its Replays tab lists the sessions its replays keep, newest first, each with when it started, how long it runs, its marks and its size. Then it lists the saved clips and how much of the budget they use. It reads them through the game’s Vite, as spawnite replay list reads them. ⏎ opens a session: a timeline of the minutes it keeps with a ▲ under each mark, and its moments. The moments are the marks first, and then one every 10 s that has a line of the log within 5 s, so an idle stretch shows none. Beside them is the shot kept nearest the selected moment and its log within 5 s either side. ⏎ or o writes the shot to the temp folder and opens it in the machine’s viewer, and c saves the minutes around the moment as spawnite replay save --around does.
Its Publish tab shows the game’s state and its link, above its versions on Spawnite, newest first, with the live one marked. It runs the code the commands run, with their progress and their refusals in the tab:
ubuilds the game and uploads it as the next version, asspawnite uploaddoes.pmakes the selected version live, asspawnite publish <version>does.vputs the game on the store or takes it off, asspawnite visibilitydoes.
p, and v when it would put the game on the store, ask first: the tab names the version or the game, and the same key pressed again acts, while esc or any other key cancels. Signed out, the tab says how to log in. For a game whose package.json names no id yet, it says to run spawnite games create <name> in its folder.
New game shows the registry’s templates as spawnite list reads them, one card each with its description and whether it plays solo, multiplayer, or solo or multiplayer. ⏎ picks one and asks for a folder name of letters, digits, - and _, where q types a q. It asks under a preview of the files it writes: each file at the top, then each folder with how many files it holds, which the wheel scrolls. ⏎ again writes the game to games/<name> as spawnite create does and adds it to the Games panel.
The account shows who the machine is signed in as and where the login is kept, and logs in or out as spawnite login and spawnite logout do, with the code to check on screen while a login waits. Signed in, n asks for a new nickname, where q types a q, and ⏎ sets it as spawnite account name does, or shows why the backend refused it.
Without a terminal on both stdin and stdout, or with CI set, pnpm spawnite prints the help and exits 2 instead.
Shell and dashboard
Section titled “Shell and dashboard”npx @spawnite/cli plugin installconnects the Spawnite plugin to every coding agent on thePATH. In Claude Code, it adds thespawnite/pluginmarketplace and installsspawnite@spawnitefor the user. It enables one that is installed but disabled, and installs one that is only a project’s for the user. In Codex, it registers the MCP server ascodex mcp add spawnite -- npx -y @spawnite/mcpdoes, and adds the long tool timeout under its table in~/.codex/config.toml. Each agent loads the plugin when its next session starts, or Claude Code when the user types/reload-plugins, and the reply says so; nothing waits on it. With neither agent on thePATH, it refuses and names where to install one.--jsoncarries what was done for each agent underconnected.pnpm exec spawnite completion installputsspawniteon yourPATHand has bash complete its commands. It writes~/.local/bin/spawnite,init.shin the data folder env-paths names (~/.local/share/spawniteon Linux), and one line in~/.bashrcand your login file.spawnite dashboard screenshotdraws the dashboard with no terminal, for the games under the working folder, presses--keysin order, each once the screen has settled, and prints the screen as text.--keys j,down,enter,esctakes a character or the name ofup,down,left,right,enter,esc,tab,spaceorbackspace.waitpresses nothing but holds the next key until the screen draws something new, such as the replays a tab reads through the game’s Vite:--keys right,wait,enter.--size 100x30sets the terminal’s columns and rows.--until <text>waits up to 10 s for the screen to show the text, such as an account a network read fills in, and fails with the screen it showed.--out <file>writes the screen as a PNG in its colours instead, shot in headless Chromium.--jsoncarries the text asscreen.
Account
Section titled “Account”-
spawnite loginsigns this machine in to Spawnite through WorkOS AuthKit’s device flow, asgh auth logindoes. It opens the approval page in the browser, with the code filled in, prints the address and the code to stderr, and waits until the code is approved there, on this machine or any other. An agent runs it itself, since only the approval needs its user, and in a sandbox or over SSH hands them the address. Signing in the first time creates the account.--no-browseronly prints.The login is kept in
login.jsonunderSPAWNITE_CONFIG_DIR, else under the platform’s config folder as env-paths names it. That folder is~/Library/Preferences/spawniteon macOS,%APPDATA%\spawnite\Configon Windows, and$XDG_CONFIG_HOME/spawniteor~/.config/spawniteon Linux. The file is readable by its owner alone, and its access token is refreshed when it expires. The backend is production,https://api.spawnite.com, for a cli installed from npm, and the dev deployment for a run from this repository’s checkout.SPAWNITE_CONVEX_URL, a backend’s Convex API address, names another for either, such ashttps://api.spawnite.comfor production from the checkout. The backend names the AuthKit client. -
spawnite logoutends the machine’s session with WorkOS, through the backend, which holds the API key WorkOS asks for, so a copy of the refresh token is spent. It then forgets the kept login, even when the session cannot be ended, and says why not: the backend refused, or the login was made against another backend, whose session is left to expire. A session whose refresh WorkOS already refuses counts as ended. -
spawnite whoamiprints the account the machine is signed in as, once the backend accepts its sign-in, its nickname and handle, whether the account still needs a nickname, and where the login is kept. It refuses when there is no login, and when the backend refuses it.
Publish
Section titled “Publish”-
spawnite games create <name>creates a game on the signed-in account from a name. The name is up to 50 characters, with no word the store does not allow, and other games may share it. The command prints the game’s id and its slug, the readable text in its address, which is made from the name, as Publishing a game says. Run in a game’s folder, or with--project, it writes the id into the project’spackage.jsonas"spawnite": { "game": "<id>" }, so the commands below find the game there. It refuses a project that already names a game, and--no-projectwrites the id nowhere. -
spawnite upload [folder]builds the game into itsdist/bundle, asspawnite builddoes, and uploads the bundle as the game’s next version, on the signed-in account. Given a folderspawnite buildwrote, it uploads that folder as it is. Before it sends anything, it checks the bundle against the upload’s limits, the same numbers the backend reads from@spawnite/schema’sbundleLimits: 1 GB in all, 5,000 files and 50 MB of the page’s HTML, scripts and styles. It refuses with every finding, one per line: the limit, the bundle’s value, the limit’s value, the largest files or fullest folders, and what to change. Past 100 MB in all, it uploads and prints a warning that says why the size matters and what to check. It warns the same way of each file the page loads from another site, which a published page refuses.It then asks
https://assets.spawnite.comfor each shared file the bundle names by URL. It refuses a bundle that names one the site does not serve, or when it cannot reach the site, naming the file that holds the URL. WebAssembly is allowed. The backend checks the listing and the manifest again, then signs one URL per file. The cli sends each file straight to the games bucket, eight at a time, each up to four tries. Then the backend checks the bucket holds them all and records the version.spawnite publish, and the MCP’supload_versionandpublish_versiontools, upload through the same check.It prints the version’s number and the command that makes it live: the version is checked and not live.
--engine,--pinand--no-pinchoose the build, asspawnite buildtakes them. Publishing a game says what the folder holds and what the check refuses. -
spawnite publishbuilds the game, uploads it as the next version and makes that version live at the game’s link, in one command, then prints the link, the live version and the game’s state. A first publish is unlisted: the link carries the game’s key, and only people with it can play.spawnite publish <version>makes an uploaded version live as it is, newer or older than the live one, as a rollback, and builds nothing.--publicalso puts the game on the store, and refuses before it builds a game with no store page. A publish never takes a public game off the store.--slug <slug>sets the readable text in the game’s address before anything is built. Players load the new version within a minute.Before it uploads, and before it makes an uploaded version live, it compares the version’s save entry with every version the game ever made live, at the highest version each part of the save had. It refuses, naming the part, a version below that one and the same version with another schema fingerprint. The refusal says to raise
versionand add a migration step. No flag publishes past it. A fingerprint it cannot compare, missing or in another format, prints a warning and the publish goes on.spawnite uploadalone runs no such check, since an uploaded version is live for nobody. -
A read-only command reads a published game’s numbers on the signed-in account, for the game and for each published version:
- its players
- its sessions and how long they last
- how many players came back the next day and the next week
- rounds finished, where the game has rounds
- purchases, with what was earned
It prints them as text, or as JSON with
--json. The numbers are totals for the game, start with its first player, and identify no player, as What you can see about your game says.
Create and add
Section titled “Create and add”-
spawnite create <folder> --template <name>writes a new game project from a template in the registry. The project installs the published packages at the cli’s own version. It plays with the install and dev commands of its package manager, such asnpm installthennpm run dev. That manager is the one a lock file or apackageManagerfield above the folder names, else the one that rancreate, such as the npm behindnpx. The project getspnpm-workspace.yaml, with pnpm’s own settings, only when pnpm installs it, and--tarballsrefuses any other manager.The project starts with the template’s model files and the manifest baked from them. Its
modelsscript bakespublic/assets/intosrc/models.json, itsdevandbuildscripts bake first, and its dev server rebakes when a model file appears, changes or goes. It also carries the template’s tests undertest/, all but the browser smoke, any test that imports an internal entry of ours, which the published packages drop, and any test that imports such a test. Itstestscript runs them with Vitest against the manifest as the project holds it.The project is a git repository with one commit of what
createwrote, and its.gitignoreleaves outnode_modules,dist,.replaysand.spawnite. ItsAGENTS.md, which itsCLAUDE.mdimports, does four things:- It tells the agent to prefer what the engine ships over its own code.
- It holds the index of the engine’s guide pages that
spawnite ai-fileswrites. - It tells the agent to run the project’s
checkscript before it says a step is done. The script, spelled for its manager such asnpm run check, runstscand ESLint with the engine’s rules for games. - It says that nothing backs the project up until it is pushed, so the agent offers the creator a GitHub repository for it.
Where git is missing,
createstill writes the project and says so in one line. In a folder that is already inside a git repository, it starts no second one, and says to commit and push the project there. Without--template, it lists the templates and asks.--release <version>pins another version, and--tarballs <folder>installs frompnpm packoutput instead, for a check before a tag.--roommakes a game that plays alone, such as the example, a game its friends play together. The project’spackage.jsongets aspawnite.roomnaming the scene the room runs and its settings, and gets@spawnite/roomas a dev dependency. A room game’s template, such as the arena, gets both without--room, and a template that cannot run in a room refuses it. A room game plays with the install, thenspawnite play startthrough the manager, such asnpm exec spawnite play start, which starts its room beside the dev server. -
spawnite ai-fileswrites the index of the installed engine’s guide pages into the game’sAGENTS.md, between itsBEGIN:spawnite-engine-indexandEND:spawnite-engine-indexcomments. The index holds the pages’ folder undernode_modules/@spawnite/engine, then one line per folder naming each page and the first two things a creator asks for that it answers, from the page’suseCases. A last line says where a link inside a page leads. It makes the file where it is missing, keeps everything outside the comments, and leaves the file as it is when the index is already current.It then writes the Spawnite plugin’s skills,
game-builder,model-builderandwiki, under the game’s.agents/skills/, where Codex reads a project’s skills. It replaces each of those three folders whole on every run, and keeps a skill of any other name. The cli’s build copies them frompackages/plugin/skills, andcreatewrites them too. A created game’spostinstallruns it, so the index matches the engine, and the skills the cli, that each install of the game’s packages brings. It refuses a game with no engine installed. The game is found from the working directory, or named with--project. -
spawnite upgrademoves the game to the newest release. It sets every@spawnitepackage the game lists to that version together, and runs the install of the package manager whose lock file the game has. It then prints each change between the two versions, oldest first: what was renamed or removed, and what to write instead. A game on a beta, such as0.1.0-beta.8, moves to the newest version under npm’sbetatag, or to the full release once it ships; a game on a full release followslatest. It refuses a game whose engine comes from a workspace or a folder.--jsonprints the two versions, the packages it moved and the changes asmigrations. The changes come frommigrations.jsonin the cli the install brought, so they need no network beyond the install. -
spawnite listprints what the registry holds. First come the games in three groups: those that play in a room, those that play alone or in a room with--room, and those that play alone. Then come the partsspawnite addwrites, then the games that cannot install from npm yet, each with the package it needs that no release publishes.createrefuses those from the registry. With--json, each game carriesroom:always,optionalornone, and a game that cannot install also carriesunavailable:{ "reason": "unpublished-package", "package": "@spawnite/auth" }. A game with nounavailableis onecreatewrites. -
spawnite add <item> [name]writes an item’s files into the game and prints thepnpm addlines for its dependencies. With a name,Featurein the template becomes that name: PascalCase for a behaviour, an entity, a panel, a hud part or a shader, kebab-case for a scene. The lines that register a scene, a panel or a hud part are printed for you to paste.spawnite add behaviour <Name> --systemalso writes the behaviour’s system beside it,src/behaviours/<Name>System.ts, a rule the step runs over every entity with the trait. It adds the system as the last rule of the game’s own plugin,src/rules.plugin.ts, and writes that file where the game has none. The first time, it prints the lines that list the plugin insrc/game.ts. The rule’s entry declares norunsOn, so in a game with a room the room alone runs it.spawnite add hud <Name> --slot top-rightwritessrc/hud/<Name>.tsx, aPanelat that slot of the screen with aTextand the player’s healthBar, and prints the<Name />line to put inside a<Hud>.spawnite add dialog <npc>gives the NPC atsrc/npcs/<npc>.tsxa conversation insrc/npcs/<npc>Dialog.tsxand prints the lines that place it inside its<Npc>; it refuses an NPC with no file there, or one that already holds a<Dialog>.A file that exists is never overwritten.
createandaddrefuse an item whose file would land outside the game, under a.gitfolder, whose hooks git runs, through a symlink, or in a file stream named with a colon, and write none of its files.--dry-runrefuses what a real run refuses, prints the same lines and each file’s content, and writes nothing; it does not apply tospawnite add asset. The game is found from the working directory, or named with--projectas a path or a folder undergames/. -
spawnite add asset <id>downloads a shared asset from the manifest into the game’spublic/assets/folder, so the game’s own deploy serves it, and refuses a file whose sha256 differs from the manifest’s. It refuses apublic/assets/folder reached through a symlink, asadddoes.For a model it bakes the game’s
public/assets/folder intosrc/models.json, which measures the model. It then registers the model’s entry from that manifest, with no size in it, through the game’ssrc/models.ts. That is the module the scene imports, so a room registers what a page does. Asrc/models.tsthat loops over the manifest registers the model already, and the command prints nothing to paste. One that names its entries one by one gets theregisterModelline to paste, with whichever import it lacks. A game with none gets one written that loops over the manifest, and the line that imports it into the scene the game’sspawnite.roomnames, or into the app file of a game with no room.<Entity model="<id>">then draws it.For a clip it prints the
registerClipline to paste, with the served path, and the marks to add oncespawnite play timeline --clipshows the frame. For a particle effect it writessrc/particles/<id>.jsoninstead, with its sprites inside it. It prints the line that imports it into the scene and the call that plays it:useParticles().spawnfor an effect that plays once,<Emitter>for one that loops.--assetspoints it at another base URL, or at a folder holdingassets/index.json;ASSETS_URLsets the default.
Assets
Section titled “Assets”-
spawnite assets publish <path>publishes a model, an avatar, a clip, a texture, a sound or a particle effect to the shared assets on the signed-in account, so any game adds it withspawnite add asset. It publishes one file, or every such file under a folder. A.glbor.vrmis at most 50 MB, and a.vrma,.webpor.mp3at most 20 MB, and a three.quarks effect’s.jsonat most 5 MB.Each file needs a row in the
catalog.jsonof the folder, under its path from that folder: its name, category, licence and source, with optional tags and style, as the assets page shows. The command reads what the file tells: its triangles, clip names, whether it is rigged, its bounds, a texture’s resolution, a sound’s length and whether an effect loops. It sends the row to the backend, then sends the file to the upload URL the backend answers. An effect goes with each sprite it names beside it inside the file, and with a picture of it playing for its store card, which the command renders in headless Chromium. An effect three.quarks cannot parse is refused before anything is sent. The backend checks the file before it is public and draws the asset’s id from its name, such asflat-rock-48201973.The command prints one line per file:
published, with the id and thespawnite add assetcommandpresent, for a file the account published before, which is sent nowhererefused, with the reason: the backend’s, or the cli’s own for a file it cannot read as its type
It exits 1 when a file was refused. A refused file stops no other, and a file’s name need not be an id:
Flat Rock.GLBpublishes. An account publishes at most 100 assets a day, and each counts against its storage. A file with no catalog row stops the publish before anything is sent. Needsspawnite login.--jsoncarries the files asassets.
Models and sounds
Section titled “Models and sounds”-
spawnite model make <name>runsmodels/<name>.py, a Python script, in the installed Blender with no window, and writespublic/assets/<name>.glb. The script starts from an empty scene and builds only the model. The command then exports the whole scene with modifiers applied, animations included and +Y up, rebakessrc/models.json, registers the model throughsrc/models.tsasspawnite add assetdoes, and renders a Workbench still into a temporary folder. Applying modifiers drops shape keys: they survive only on a mesh whose one modifier is its armature.It prints the file, its triangle count, its clip names, its size and the still’s path. Then it prints the concept the game keeps beside the script as
models/<name>-concept.png,.jpgor.webp, with thespawnite model checkthat lays it over the model. Then come asrc/models.tsit wrote and the lines to paste, asspawnite add assetprints them. A script that throws stops the command with Blender’s traceback, which names the script’s line. Blender isBLENDER_PATH, elseblenderon thePATH, else the folder the OS’s installer puts it in. With none, the command prints the install command for the OS:brew install --cask blender,winget install BlenderFoundation.Blenderorsudo snap install blender --classic. No game needs Blender:dev,buildand the bake never call it. -
spawnite model check <files...>reads GLB files, such as ones from Meshy, Tripo or Sketchfab, against the Models page, and draws them on one sheet to judge against their concept. A.vrmaclip among the files prints its motion table instead: its length, where the arms peak, which a release mark starts from, the hips’ drop and travel, and each humanoid bone’s largest turn and the second it peaks. A clip renders nothing. For each file it prints the triangles it draws, its size in metres along x, y and z, its clips with their lengths in seconds, its bones and each warning the bake would give it, all measured as the bake measures them.It then renders a sheet in headless Chromium, into a temporary folder, with a column for each
--angle, given once for each. An angle is any anglespawnite play screenshottakes:front, the model’s +Zthree-quarter, 45° round toward +X at eye levelright,back,left,top,bottomorisoazimuth,elevationin degrees
Without one, the columns are front, three-quarter, right and back. Every tile is orthographic at one scale, each file one unit tall.
--style shaded,silhouette,outline,wireframe,verticesdraws a band of the sheet for each:- shaded, by default
- silhouette, for proportions
- outline, for the outer edge and creases with hidden lines removed
- wireframe, for the edges with the faces in front hiding those behind
- vertices, for the points a face does not hide
--reference front=concept.pnglays a concept image on a plain or transparent background over the rest pose’s front tile, in red. It prints the overlap of the two silhouettes, their intersection over their union, then:- each silhouette’s runs across at ten heights
- the heights of the crotch, the shoulders and the neck on each silhouette
- the three heights where the body, the run on the centre line, sits furthest from the concept’s, in centimetres
A line says when the arms stand apart at other heights than the concept’s, as a T-pose concept’s do beside an A-pose model.
--reference front,right,back=turnaround.pngcuts a turnaround’s figures, left to right, at the widest gaps between them, and#x,y,width,heightafter the image crops it first. A side or three-quarter figure is also tried mirrored, and the line says when that matched better. Without--angle, the references’ angles are the sheet’s.--fitalso fits each concept by the scale and offset that overlap best, for a concept whose height an antenna or a raised weapon sets. It prints the fit, and reads the widths, the landmarks and the red outline from the fitted concept.--clip jump --at 0.4,0.9adds a row of the sheet for each moment of the clip under the rest pose’s. It also checks the whole clip 30 times a second for a forearm, hand, shin or foot more than 1 cm inside the head’s or the torso’s faces, naming the limb, the seconds and how deep. The upper limbs that sink into the body round their joints follow on one line after. A rig whose bones name no head, torso and limb is said to be unchecked. A file without the clip stands at rest, and the output says so.A
.blendis refused: export it as a.glbfirst. It refuses an angle asked twice, a reference whose image holds fewer figures than its angles, a moment past a clip’s end, and a sheet past 64 tiles. When Chromium cannot download, or a page fails, it prints the rest and says why there is no sheet.--jsoncarries the numbers ascomparisonsandcrossings. -
spawnite model avatar <file>turns a character rigged by Meshy into a VRM 1.0 avatar that plays every VRMA clip. It maps Meshy’s 22 humanoid bones onto the VRM humanoid, stands the skeleton in a T-pose facing +Z with the palms down, binds the mesh in that pose, bakes Meshy’s centimetreArmatureinto metres and leaves the clips out. It writes<file>.vrmbeside the GLB, or--output. A rig that lacks one of Meshy’s bones is refused with each missing bone’s name. -
spawnite sound check <files...>reads sound files with the installed ffmpeg, in any format it decodes, and prints each file’s length, peak, RMS loudness, and the level and spectral flatness of its quietest tenth of 46 ms frames. A sound of 1 s or more carries a constant noise, such as a recording’s hiss, when that tenth is louder than −65 dBFS and at a flatness of 0.6 or more; a shorter one is not judged, since its quietest tenth is its own decay. Assets says how the numbers were set. With no ffmpeg on the PATH, it refuses with the command that installs it.
-
spawnite buildbuilds the game’s bundle, the version as the store keeps it, into the game’sdist/bundle, or the folder--outnames, emptied first, and sends nothing. The bundle is laid out as the store keeps it:page/, the page, with the engine and its peers left for an import map at an engine releaseserver/, the server halfmanifest.json, which names the engine release, when the game plays in a room and its player cap, read from thespawnite.roomin itspackage.json, and every other file by its size and SHA-256
It builds against the release in the installed engine’s
dist-browser, or the release folder--enginenames. It fails where either half imports something the release lacks, naming the file, the import and the release.It prints, in order:
- the bundle’s hash, the SHA-256 of its files’
sha256sumlisting - each half’s size
Game code, the gzipped size of the game’s own code a player downloads before the game starts- the heaviest of the files a player downloads then, each from the page or the release, and a release file with the packages it holds
- the
--topheaviest modules of the game’s own code by rendered size, 10 by default - last, whether the bundle is within the upload’s limits, or each finding
spawnite uploadwould refuse it for - then each warning the upload would print
The warnings include one past 500 KB of game code gzipped, since a first-time player on a phone waits a second or two longer. They also include one that names each file the page’s HTML, stylesheets or scripts load from another site, since a published game’s page loads files from its own origin, the engine’s address and the shared assets site alone.
--jsoncarries the manifest, the game’s code bytes ascode.gameBytes, the engine’s ascode.engineBytes, every file undercode.files, the findings asrefusalsand the warnings aswarningstoo. A bundle past a limit or a warning still builds, and the command exits 0.spawnite uploadandspawnite publishname the game code in theirBuilt the bundleline and print its warning with the upload’s.The pin lives in the project’s
package.jsonas"spawnite": { "engine": { "pin": "exact" } }, which every build reads,upload’s andpublish’s too. A pinned version keeps the release it is built against, and gets no engine fixes, when the platform serves a later one. An unpinned one runs the release the platform serves.--pinwrites the pin intopackage.jsonand builds pinned;--no-pinremoves it and builds unpinned. With neither flag, any other value underspawnite.engineis refused before the build; either flag replaces it. It names each shared file, from@spawnite/assetsor the engine, by its URL onhttps://assets.spawnite.comand copies none, as a published page loads it. It sends nothing to check that the site serves it, so it runs offline.--serve <port>then serves the page at/and the release at/_engine/<release>/, until stopped. The site serves the release from the engine’s address instead, sospawnite play join --urlcan join it to a room.spawnite build --serverbuilds only the server half a room runs: each scene undersrc/scenesas one.mjsmodule named for its file, with the engine and its peers left for the room’s own copy and no asset written. It writes them into the game’sdist-server, emptied first, or the folder--outnames, where nothing is removed. Beside them it writesbuild.json, the game’s build, which a room on the half compares a joining page’s with: copy it with the modules. It prints the half’s bytes and each file. Publishing says more.
Simulate
Section titled “Simulate”-
spawnite simulate --scene <name> --seconds <n>runs a scene in Node with no browser and no GPU, and prints the world’s dump with its hash and the steps’ wall time.--until <expression>steps until the expression holds, over the sameentities,count,has,playerandstepsthe playtest’s step reads, within--budgetseconds of game time. It exits 1 when it did not.--keys w,aholds keys down for every step;w,a,s,dand the arrows steer the player’s character, andSpacejumps it once. A key bound to a command, such as a dash on F, sends the command once, through her character. The run joins her player before the scene mounts, as a page played alone does, so the scene’s<CharacterSpawn>hands her her character.The command loads the engine and the scene’s file,
src/scenes/<Name>.tsx, through the game’s own Vite. It mounts the component the file exports under that name through the engine’s headless mount, so nothing draws, no model or sound loads and no UI mounts. The components render between calls, not between steps, so a modal that opens on a step, a round screen for one, holds no step up: the round’s state is in the dump. Five seconds of game time take milliseconds, and two runs on one input print one hash.--profiletimes each system of the step and prints, slowest first, its mean and worst milliseconds per step over the last 60 steps;--jsoncarries them asprofile, and the hash is the same with it or without.--fields transform,wallet.coinsprints only the traits and fields it names, and the entities that hold one, in place of the whole dump, with a line naming each field no entity holds;--jsoncarries the same narrowedentities.--aiprints the AI tree after the steps in place of the dump: each described entity in words, seen from the player or from the entity row--subject <id|name>names, as a JSON list with one node a line.--jsoncarries it asaibeside the dump.--save <file>starts the world from a save file, as a page loads a player’s save: the world’s saves and the persisted stores restore before the scene mounts, and her character’s as she spawns. A file the game would refuse stops the command with the part that failed.
-
spawnite play installdownloads the Chromium the playtest drives, for the Playwright version the cli runs, into Playwright’s folder, which every game on the machine shares. Every command that opens Chromium downloads it the first time it finds none, with the progress on stderr, so run this one only to fetch it ahead, such as before going offline, or again after a download failed. -
spawnite play startbakes the game’spublic/assets/folder intosrc/models.json, starts the game’s dev server, opens it in a headless Chromium with the world held still, and prints its URL and the devtools tree. A game with no model files and nosrc/models.jsongets none. For a game whosepackage.jsonnames aspawnite.room, the command also starts the game’s room on a free port, with the rest of the environment itsenvsets, such asMAX_PLAYERS, and passes that address in the page’sroomparameter. It waits until the tree holds the player’s character the room streams before it prints the tree. It fails when she has not shown within 90 s. The room, the dev server and the browser start side by side, and the page loads while the room starts.The browser keeps the shaders it compiled for the game’s last session under the game’s
node_modules/.cache/game/play/profileand nothing else of its profile, so a game’s saved storage never carries from one session to the next. The printed URL carries the room’s address.--scene <name>goes on from the game’s start scene to the scene registered under that name, as a button that opens it would, and prints that scene’s tree. Every session opens held and prints where its clock stands: a game’s world before its first step, or a room game’s room, which the session holds with its page once the page has joined. The room is started withROOM_DIRECTOR=1, which lets the cli hold its clock.--bots <n>adds the room’s bots, asspawnite play roomruns them, which play until the session stops. The page is a player too, so a game that waits on every player, such as a run that starts once each is ready, waits on it.--page-bothas the page’s own character play the part the scene’sbotplays for a room bot: the words a player’s keys and menus send, while its walk and its aim stay the agent’s. With--pages, each page’s character does.--pages <n>opens more players’ pages in the room, the session’s own among them, each a tab of its own in the session’s browser that joins before the bots and holds and steps with the room.--page <n>onscreenshot,dump,eval,stateandlookreads the nth.--invulnerablespares every player’s character, and--room-env NAME=VALUEsets the room’s environment.--latency <ms>,--jitter <ms>and--stall <ms>/<every s>play each page over a slow link to the room: the round trip, how much each message’s trip varies, and a stall now and then. They are named in the page’s address and onplay state’sRoom:line.--replay <replay>plays a replay back on the game’s dev page instead, with the code as it stands, and--at <moment>stands it on a moment.--device <name>opens the pages as a device Playwright names, such as'Pixel 7'or'iPhone 15': its screen’s size and pixels, a touch screen, and a phone’s coarse pointer.--query <parameters>adds parameters to the address of each page the session opens, such asdebugfor a game that mounts its devtools behind?debug, or'map=town&debug'. The session’s own, such asroomandhold, are set over them. The replay page says how to direct a session.The session stops itself once it goes
--idle <minutes>with no command on it, 60 by default;--idle 0keeps it untilspawnite play stop.--save <file>starts the session from a save file in place of a new game: the page’s save in a game with no rooms, and every player’s in a room game’s room. The file is handed over once, so a reload or a rejoin keeps what was played since, and nothing is written over the file; without it the session starts clean. While a session of the game thatspawnite play startbegan runs, a second start exits 1 and names its address and who started it, because someone may be playing it or driving it.--replacestops that session and starts in its place. A session whose dev server has ended counts as none. -
spawnite play stepsteps the running playtest--steps <n>fixed steps,--seconds <s>of the game’s time, or until--until <expression>holds, and leaves it paused. A room session steps its room and its page together; toward a condition, it lets the room run at its own pace and holds it on the first frame the page sees the condition hold. A replay plays on, fast toward a condition, and then plays the last half second before where it stopped again at the page’s pace, so what the page animates in its DOM stands as it stood then. The expression readsentities,count,has,playerandsteps, and the engine’s owncamera,loop,cursorandroom, asspawnite play stateprints them. In a replay, it also readsat, the moment the page stands on in seconds, such as--until 'at >= 58.55'. A pausing modal stops it early, and the command names the modal. It exits 1 when the condition did not hold, and says so when the round never started, naming the input that starts it.--keys KeyW,MouseLeftholds controls down while it steps, any the input layer names, below, and a control held already stays held after.--press <control@seconds>lands inputs on moments of the step,control@secondsa press andcontrol@from-toa hold, in seconds of the step’s game time, such as--press MouseLeft@0-0.5,KeyR@1, the session held between.--sample <expression>reads the expression on each frame the page draws, asspawnite play evalreads one, and prints each frame’s value beside the world’s step and the canvas’s clock, such as--seconds 0.8 --sample camera.pitchfor a recoil’s curve. A room session runs with its page at real time for it. A replay plays on with the player’s input on top of what it recorded. Your first game walks through a playtest. -
spawnite play record startandspawnite play record stoprecord a video clip of the session’s page, as the dev tools’ Record button does, into the game’s.spawnite/clips: an H.264 MP4 with its stills, contact sheet andclip.json. Between them, any play command moves the world, such asspawnite play step --seconds 4 --press KeyW@0-3.spawnite play record --seconds <s>records that much of the game’s time in one go, with--pressasspawnite play steptakes it.The clip runs on the world’s steps, one frame a step at 1/60 s, so it shows the game’s time with no pause while a command waits, and drops no frame: a held world waits for the encoder. Its time is not the replay’s, so it keeps no replay window. Holding the world pauses the clip: a held session records nothing between commands, and
spawnite play pausestops a running one’s clip until the nextsteporresume.--shape 9:16letterboxes the page to a tall clip; 16:9 is the default. Stop prints the clip asspawnite clip showdoes. A page that plays a replay back records none. -
spawnite play pauseholds the session where it stands: a game’s world, a room game’s room with its page, or a replay’s playback. -
spawnite play resumeruns the session on, at--speed <factor>of real time, 1 by default. -
spawnite play seek <moment>stands the session on a moment, held: seconds, a mark such asm1, a mark and seconds such asm1-2, ornowand seconds such asnow-5. A replay goes back as well as on, stands her character where its keyframe had her, and says when her character stands more than a centimetre from where the recording’s keyframes had her. A room session opens its page’s own replay in a second page, andspawnite play seek livegoes back to the room. -
spawnite play markmarks the moment on the page’s own replay, as F8 does. -
spawnite play press <controls...>presses each control and lets it go, in order, on the page the session shows, as a player does. A control is one of these:- a key, by
KeyboardEvent.codeor Playwright’s name, such asKeyW,Digit1,SpaceorShift MouseLeft,MouseRightorMouseMiddle- a standard pad’s
PadAtoPadHome - a finger,
Touch1toTouch10, which lands at--at x,y
Shift+KeyWis a chord.--hold <seconds>keeps each down that long in the game’s time, which a held session steps. Without it a control goes down and up at once, which a game that reads a control held each frame, such as a trigger, misses.--at x,ypresses a mouse button at a point of the page, in its pixels.--on <subject>presses on an entity where it shows nearest the middle of what shows, asspawnite play seereads it, such asspawnite play press MouseLeft --on '[npc.name=Mira]'to walk to an NPC and talk. A subject that picks several entities, or one that shows nothing, fails saying why.While the engine holds the cursor captured, which it does with no pointer lock in an automated browser, the mouse reaches the canvas at its middle, as a pointer lock delivers it. Keys, and the mouse over a free cursor, are the browser’s own trusted input; a finger is Chromium’s touch input; a pad is a standard pad the page’s
navigator.getGamepads()returns.spawnite play press Digit1takes a card where a game binds 1 to one. It prints what is held after. - a key, by
-
spawnite play hold <controls...>holds each control down untilspawnite play releaselets it go, across later commands, so astepor aresumeplays with it held. -
spawnite play release [controls...]lets each control go, or with none everything held, the last held first. -
spawnite play move <control>moves what moves:Mouse --by x,y, which turns the camera while the cursor is captured, or--to x,yfor a free cursorWheel --by y, 100 a notch downPadLeftStickorPadRightStick --to x,y, each from -1 to 1PadLTorPadRT --toa pull from 0 to 1- a finger held,
--toor--by
-
spawnite play see [subject]prints what the player’s camera sees of each entity a subject picks, or of every entity it draws, most showing first. For each, it prints the share of it that shows with the rest of the scene in front, its pixels showing and in view, the box they fill on the page and its share of the screen.--jsonalso carriespoint, where it shows nearest the middle of what shows, whichpress --onpresses. It also carriescoveredBy, the element the page draws over that point, such as a dialog, which takes a click there and makespress --onrefuse.--part <name>narrows each to the objects inside its drawn object whose name holds the text, such as the gun in a character’s hand.--jsoncarries it assightings. -
spawnite play aimprints where the crosshair points: the first thing the camera’s ray through the canvas’s middle meets, by the engine’s own reads of the targets it draws and of cover. It also prints each shot the page sent its room, the newest twenty, beside where the crosshair pointed as it left, with how far the shot’s line passed that point in metres and degrees. -
spawnite play set <subject> <path> <value>writes a trait’s value onto the entities a subject names, a dump key or a selector such as'[controlled]', so a check starts from the state it needs:spawnite play set '[controlled]' warden.health 0. The path names the trait asspawnite play dumpfiles it, or a field of it, and in a room also as its module exports it, such asSiegeStateTrait; the value is JSON,nulltakes the trait off, and anything else is text. A room takes the write between two steps and records it, sospawnite replay rerunandchecktake it again, and a held room steps one move so its page draws it. A walker whose transform it writes stands there at once:spawnite map query --at x,zreads the ground’s height there. An entity the page keeps alone, whose dump key startslocal:, such as its camera, is written on the page, sospawnite play set '[camera]' camera.fov 60tries a lens with no edit. A replay refuses one. -
spawnite play from-here --at x,zstands the game’s player on the ground at a point and plays from there, as the map editor’s Play from here does. She stands at the ground’s height under the point, facing the way the camera looks, out of any prop the point stands inside. On a map the scene shows she is the player who stands there;--map <name>names another, which opens in its preview, where the game’s own player spawns. The session stays held, sospawnite play stepplays it. A room places its players and refuses;spawnite play set '[controlled]' transform.position '[x,y,z]'has it move her. -
spawnite play lookturns the player’s camera on the page the session shows, to--yawand--pitchin the degreesspawnite play statereads, or by--turndegrees of yaw, and prints where it looks. -
spawnite play screenshotshoots the page as a PNG, saves it under the game’s.spawnite/shotsbeside the devtools page’s shots, and prints its file: what the player sees, the HUD among it, or with--no-hudone render of the game’s camera.--untilsteps the world first, and--view,--angle,--aroundand--zoomplace the camera.--aroundalso takes an entity by its dump key, by a selector of what it is such as'[monster.kind=colossus]', or by its row’s name in the tree, framed where it stands.--eye x,y,z --target x,y,zplaces a free camera,--orbit <n>takes n shots round the subject, and--framing shoulder,toporwideframes the player’s character or a subject for you.--sizesets the page’s size for the shot. A list such as--size 1920x1080,1280x720,960x540shoots at each in turn in one call, with the world held between them. What the page animates on its own clock moves on a few hundred milliseconds from one size to the next.--out <file>writes the shot there rather than under.spawnite/shots; several shots add their place in an orbit and their size to its name, such asoffer-1280x720.png.--widthwrites each file at most that wide. Each shot from a camera other than the player’s own prints the camera it resolved to: its lens, its eye and its target. It also prints which world direction is up and right on the image, such asscreen up = -z (north), right = +x (east)from--angle top. A shot from any camera but the player’s own, and--no-hud, leaves out the screen overlays her view draws in the canvas, such as a hurt vignette, and--overlayskeeps them. -
spawnite play timeline [controls...]presses the controls, or with--clip <name>plays a clip the game registered on the player’s character and prints theregisterClipline to write its mark into. It then shoots the page every--everyseconds of the game’s time, 0.1 by default, for--seconds, 2 by default, from the press’s own frame. It tiles the frames into one PNG under.spawnite/shots, or at--out, each under its time. The result is a sheet a person reads to pick the frame an effect should land on, such as a cast’s release, and the second to give the ability.Each frame is cropped round
--crop, the player’s character without it, where the page shows it on the first frame, asspawnite play seefinds it;--crop nonekeeps the whole page, shrunk to the cell.--cell 300x420sizes each frame’s picture and--columns 5the row. The MCP’stimelinetool is the same for an agent, withkeysorclip. The gap snaps to whole steps, 60 a second, and the labels count the gap stepped. The session is held for the sheet, so each frame is exact to its step, and a session that ran runs on after; a pausing modal that opens ends the sheet at the frames shot before it. -
spawnite play dumpprints the world: every entity’s traits, by entity id, then, asviews, what each view exposes of its own state through the engine’suseInspect.--where '[monster.kind=colossus]'prints only the entities a selector picks, a trait’s field with a value or[controlled]for a trait, and takes a key or a row’s name too, as--arounddoes.--fields transform,npc.lineprints only the traits and fields it names, and the entities that hold one, asgh --jsonnarrows a reply, with a line naming each field no entity holds. -
spawnite play eval <expression>prints the value of a JavaScript expression on the page the session shows, as a browser’s console does, and leaves the session where it stands: a room session’s past where a seek opened one. It reads the page’s own globals,documentamong them. As a--untilcondition does, it also readsentities,count,hasandplayerover the world and the engine’scamera,loop,cursorandroom. For example,spawnite play eval "document.querySelector('[role=meter]').getAttribute('aria-valuenow')"reads a HUD’s value after a seek, andspawnite play eval "player().health"reads the world.A function is called and a promise awaited, for 30 s at most, or
--timeout <seconds>, since every later command on the session waits for it. A string prints as it is, an element as its HTML, and any other value as JSON;--jsoncarries it asvalue, left out where the expression returnsundefined. It also readsviews, what each view exposes throughuseInspect. An expression that throws exits 1 with the page’s error.On an engine that has it, the expression reads the engine’s whole scope, which
PlayScopein@spawnite/engine/devtoolstypes:worldandfind(subject), for the Koota world and the entities a subject picksthree, for React Three Fiber’s stateengine(), for the engine’s module, whose traits read an entityfindpicks, asfind('[npc]')[0].get(engine().Transform)load(path), for a promise of one of the game’s modulesseeandaim- the director’s
inputandtime
For example,
spawnite play eval "async () => { await input.hold('MouseLeft'); await time.step({ seconds: 0.5 }); return input.release('MouseLeft'); }"holds the trigger.--file <path>runs a script’s default export with the scope in place of an expression, a module the game’s dev server compiles, whose imports are the game’s own. -
spawnite play aiprints the running playtest’s AI tree in the same form, from the player or from--subject <id|name>;--jsoncarries it asai. -
spawnite play wiki [query]prints the running playtest’s own wiki as JSON,{ overviewPath, kinds, entries }: the parts of the game the devtools’ Wiki tab lists, such as its behaviours, traits and systems. An entry carriespagePath, its page’s path from the game’s folder, where that file exists. That is the game’s own page for its own entry, such aswiki/scene/run.md, and the page the installed engine ships for an engine entry, such asnode_modules/@spawnite/engine/dist/wiki/engine/behaviours/stats.md.overviewPathiswiki/index.mdwhere the overview exists. An entry’spropertieshold its own data as its registration holds it, such as an item’s definition, and each of itslinkscarries arelationwhere one says how it connects, such aschangesfrom an item to the trait its use changes.querykeeps the entries whose name, kind or description holds it, ignoring case, and--kind <kind>keeps one kind; a kind the game lacks fails, naming the game’s kinds.--jsoncarries it aswiki. -
spawnite play compareshoots each shot a game’stest/shots.jsonnames, stepping the world to the shot’s step from the page’s load, and compares it with the accepted WebP intest/shots. It prints each shot’s share of pixels whose any channel differs by more than 8 of 255, pass or fail against the shot’sshare, 1% without one, and the difference image it wrote to the temp folder, and exits 1 when a shot fails.--acceptwrites the shots as the accepted set. A game with notest/shots.jsonis told the JSON to write there. The MCP’scompare_shotsruns the same comparison for an agent. The performance page describes the file and when the accepted set holds. -
spawnite play statsprints what the running playtest costs to draw: the draw calls, triangles, points and lines of one frame, summed over every render call in it. It also prints the geometries and textures the renderer holds, and the mean and worst frame time over 60 drawn frames;--jsoncarries it asstats. The page is the development build, whose dev build, devtools and replay recorder add to each frame, several times over for a busy scene, so the output says so and points tospawnite play profile, which times the production build. -
spawnite play consoleprints the page’s console errors and warnings and its uncaught errors since the session started, or since the lastconsole, each led by its level, as the MCP’sread_consolereads them. A line repeated in a row is folded into one with its count. It reads a page whose scene never mounted too.--jsoncarries the lines asconsole. -
spawnite play actionslists the game’s playtest actions by name, the steps its devtools panels run, such as a quick start past the title, which a game passes to<Devtools playtestActions={...} />. -
spawnite play action <name>runs one of the game’s playtest actions as its button does, whatever the name’s case, and prints what the action says it did, such asNo damage on.. An action that refuses, such as a quick start mid-run, exits 1 with why. The world stays where it stands: step it to see what the action set going. -
spawnite play stateprints the engine’s own state on the running playtest, in this order:- the camera’s position, yaw, pitch, lens and how far it has turned since the loop mounted
- the frame loop’s mode, hold, speed, steps, frames and clock
- whether the cursor is captured
- where the page stands with its room
- the engine build the page loaded, and whether its dev server now serves a newer one
- what the player holds: the controls down, the free cursor and each finger
--jsoncarries it asstate, the engine build asengine, and what is held asinput. The devtools page lists each field. -
spawnite play profileserves the game’s build withvite previewand plays it for--seconds, 20 by default, in a headless Chromium at 1920x1080. It opens no window unless--headedtakes the screen or--windowplaces one. It walks, jumps and drags the camera as a player does. It prints the frame rate, the CPU milliseconds a frame, and each function’s inclusive milliseconds a frame, named by the source file and line the build’s source map gives. It also prints the frame times and how many took longer than--budget, 7.5 ms by default.--gpuwith--no-vsyncreads the GPU’s time from a timer query.--rendercounts each frame’s draws, triangles and instances per pass, and--skip <class>drops one class of draw to price it.--allocprints the garbage a frame makes by call site, and--tracecounts the garbage collections.--quality <level>plays at a quality level of its own, and--no-cpuleaves the sampler off. It also prints the step’s milliseconds by system over the recording’s last 60 steps, asspawnite simulate --profiledoes. Build the game with--minify=false --sourcemap=truefirst.--urlprofiles a page that is already served.--devserves the game’s dev server in place of its build, and prints each warning of its replay among the notes, such as a mark that protects nothing. For a game whosepackage.jsonnames aspawnite.room, the command also starts the game’s room on a free port, with the rest of the environment itsenvsets. It passes that address in the page’sroomparameter, for the build and--devalike.--urlstarts no room.--query <parameters>adds parameters to the page’s address, such asreplay=off.--press <key@seconds>presses a key at a second of the recording, such asF8@30, or holds it from one second to another, such asKeyW@2-5, and marks each press on the timeline.Every run also keeps a timeline from the page’s first frame. It prints each frame over
--hitchmilliseconds, 50 by default, with what it waited on: the shader compiles by material, the draw, the script by function and the garbage collection. It prints the engine’s or the game’sperformance.markbefore that frame. It also prints the bytes the page fetched and the requests it made before its loading screen lifted, which is what a player waits for, and by the end of the run. With--until, on a page with no loading screen, it prints the downloads before the expression held: the wait.--drive idleplays nothing and--drive botplays from what the engine knows.--map-camera <zoom>records under the map editor’s camera flown out to that zoom of the whole map’s frame, 3 its own landing and 6 twice as close. It serves the dev page and plays nothing unless--drivesays so.--invulnerablespares every player’s character in the game’s room.Give
--querymore than once, or--repeat <n>, and the command sweeps: it runs each--queryconfiguration in turn,nrounds, into a folder of its own under--out. It then compares each configuration with the first, asprofile comparedoes.--function <name>times one function a call: a path on the page’swindow, such ascreateImageBitmap, is wrapped and each call timed. Any other name is read from V8’s sampler, with its calls counted by V8’s coverage.Where the page exposes the engine’s state, the run also prints how far the camera turned over the recording and the steps and frames the loop took, and
--untilreads the engine’scamera,loop,cursorandroom. The room the command starts logs a heartbeat a second, and the output prints its steps over the recording beside the page’s frames. It prints how many steps it ran, a step’s mean and longest milliseconds, each step timed without a recording room’s replay checkpoint, that room’s longest checkpoint, and its memory. The run’s file keeps each heartbeat.--room-env NAME=VALUEsets the room’s environment over the target’s, such asROOM_REPLAY=0, once for each setting, with no edit topackage.json. With--dev,--latency,--jitterand--stallplay the page over a slow link; a build carries none.--until <expression>walks until the expression holds in the page and then records, within--until-seconds, 120 by default.--bots <n>plays the room’s bots in the game’s own room beside the page, asspawnite play roomplays them, from before the page loads until the recording ends.--no-jumpwalks without pressing Space, and--scale <n>sets the headless page’s device scale.--har <path>writes every request the page made, with its URL and sizes and no response bodies, to a HAR file, which names the files behind those sums. A sweep ignores the path and writes each run’s HAR beside its run file, as<round>.har.--probelists the first recorded frame’s draws by pass and program.--checkprofiles the build in one fixed configuration, walking and then with a still camera, prints each count beside the game’stest/profile-baseline.json, and exits 1 when a count rose past it. It also times a fixed spin before and after the runs. It appends a row of each run’s timings, over 6.94 ms or, for a run pinned to 60 fps, the frames that missed the pin, with the counts, totest/profile-history.jsonl. When a spin took more than 15% past the median of the spins there on the same CPU, it appends no row and printsUnder load.It prints each run’s timings beside the previous row’s on the same machine, and a pinned run that held under 95% of its rate leaves
garbageBytesPerFrameout as inconclusive. When a count rose, it names the first-parent merges since that row that touch the game,packages/engineorpackages/assets. A check waits while another holds its lock, and gives up after ten minutes.--update-baselinewrites the baseline and no history row.--update-baseline=<count>,<count>writes only the counts it names, and a baseline with nofpsgets 60. The run writes itself to a JSON file. The frame page says how to read it, and Finding hitches the timeline. -
spawnite play profile report <runs...>prints a run’s result again from its file, over--fromand--toseconds or with another hitch threshold. Several runs, or a folder of them, print as a series: each reading’s median, its lowest and highest, and every run’s value. A run that started the game’s room also reads the room’s steps over the whole run: a step’s mean and longest milliseconds, as a recording room timed each step without its replay checkpoint, and its longest checkpoint. A series and a comparison also carry the bytes fetched and the requests made before the lift and by the end of the run, where the runs kept downloads. Where no run lifted and every run used--until, they carry them before the recording started. Bytes move past 1,024. -
spawnite play profile compare <before> <after>sets two runs’ results side by side and marks each measure better or worse past the machine’s noise. Each side may be a folder of runs, read as its median, lowest and highest, and a measure moves only where the two sides’ runs do not overlap. The room’s steps sit beside the page’s frames where both sides started the game’s room, and a step’s time moves past 0.1 ms and a fifth. The bytes and requests the page fetched sit beside them, a change of under 1,024 bytes counting as the same, and a rise in the bytes before the lift is named under what got worse. -
spawnite play roomplays the game’s own room with bots and no browser, once for each bot count and seed, each run in a fresh room on a free port with the rest of the environment itsspawnite.roomsets. Each bot joins over the room’s WebSocket as a page does, turns to the nearest living entity with health, walks to it round walls along the room’s navmesh or strafes beside it, and walks a circle with none in sight. A scene that exports abotbeside its component names the weapon each fires and the game’s own messages each sends, such as the word that starts a run; Multiplayer says how.--botstakes a count from 1 to 12, a list such as1,2,4or a range such as1-4, and--seeda seed, a list or a range, from which the room’s world draws its random numbers; each pair is one run. The room holds its clock until every bot has joined, then runs at--speed, up to 16 times real time, until the scene’stimelinesays the run is over or--secondsof play are up: 60, or 1800 where the timeline says when a run is over. The command prints what each bot did and a table a run, with a row for each part the timeline’spartsname, such as each wave’s fight and breather. Each row holds when the part started and how long it lasted, each measure the timeline records, a step’s mean and longest milliseconds, and the bytes a page received a second. Then it prints a table over the runs of each bot count. Measuring runs says how to declare a timeline and read the tables.The runs go to a file as each ends,
--outorspawnite/room-runsin the system’s temp folder, which keeps the newest 20, andspawnite play room report <file>prints them again. It also names the run each room recorded, whichspawnite replay log,state,exportandcheckread with--run <run>and no browser.--invulnerablespares every player’s character, and--room-env NAME=VALUEsets the room’s environment over the target’s.--latency,--jitterand--stallplay each bot over a slow link, in the game’s time. The room runs unwatched, so an edit to the game’s files never ends a run. -
spawnite play room report <file>prints the runs a file ofspawnite play roomholds again: each run’s table, and the tables over the runs of each bot count, summed afresh from the timeline and the heartbeats the file keeps. -
spawnite play join [room]joins players’ pages to a room and prints what each saw: welcomed as its character, refused with the room’s close code and its reason read out, such as a full room, or no answer within--join-seconds, 90 by default. The room is the address given,ws://orwss://, such as a room a person or a machine started. Without one, it is the game’s own room, started fresh on a free port with a heartbeat a second and no restart on an edit, and--room-envover them. For its own room, the output adds the room’s build, its last heartbeat, its peak memory and its timeline.The pages are the game’s build, served with
vite preview, which names its build as it joins, so the line names a room’s other build beside the page’s.--devserves the dev server’s page, which names none, and--urlopens a page already served, such as the deployed game.--pages <n>, 1 to 12, opens that many, one after another, each headless in a browser context of its own so it joins as a player of its own.--press <control@seconds>lands controls on each welcomed page from the last page’s join, asstep’s--pressnames them.--secondsplays the pages on,--until <expression>plays until the expression holds on each welcomed page within--until-seconds, and--screenshot [file]shoots every page as the command ends.--seat [game]joins a room the backend runs, such as production’s, which refuses a page with no seat ticket. It takes the signed-in account’s seat in a room of the game through the backend’srooms.join. The game is named by its id or by a link to it. An unlisted game’s link carries its key, which join needs from any account but the game’s own. Without one, it takes the game the project’spackage.jsonnames, as the app does. It joins the room join answered, so it takes no room address beside it, and adds the ticket to the page’s join message. It opens one page, since an account holds one seat in a game. Nothing holds the room: the pages play live. It exits 1 when--untildid not hold. Joining a room says how to read it. -
spawnite play listlists the playtests running for the games under the folder, or the game--projectnames. For each, it shows the game’s address, a room game’s room, what started it and so what stops it, and when it started and was last used. What started it isspawnite play start, whichspawnite play stopends, or an MCP server’sstart_playtest, which itsstop_playtestends.spawnite play startnames the others running under the folder too, so an agent notices one it forgot to stop.--jsoncarries them asplaytests. -
spawnite play stopcloses the playtest’s browser, its dev server and its room. Every other command ends the Vite, room, bots and browser it starts when it ends, killed or not; the processes page says how.
Replay and errors
Section titled “Replay and errors”-
spawnite symbolicate [file]names the engine’s frames in a stack, read from the file or from stdin. The engine’s core is minified, so its frames carry no names. The command sends each engine frame, and nothing else of the stack, to the platform. The platform resolves it from the release’s private frame index to the public engine call it ran in, never to a source line. The command prints the stack with those frames named, each run of them ending in the call the game made, such asThe game called startCooldown, which failed inside the engine, from: at cool (src/systems/dash.ts:12:9).It finds an engine frame as follows:
- a frame of the engine’s library build, by
@spawnite/engine/dist/in its path, at the version the game installed or--release <version> - a frame of a published page’s release, by its address
- a frame of the dev server’s prebundled engine, through the map Vite wrote beside it
Each engine frame prints as
[engine, in <export>], a frame inside that public export, whoever called it, or[engine, its own work]; the summary line is the one that names the call the game made. The game’s own frames stay as they are. What it sends of each engine frame is the release, the file’s name inside the engine’s package, and the line and column.-reads stdin,--projectnames the game, and--jsonanswerscalls, each the game’s call and the frame it came from, andframes. It needsspawnite login, within a limit of 60 stacks an hour. - a frame of the engine’s library build, by
spawnite map place --map <name> --props <json>writes named props intosrc/maps/<name>.json. Each one is upserted, andnullremoves it.{ "asset": id }downloads a shared asset, asspawnite add assetdoes, stands it as a model, and prints the lines to paste thatspawnite add assetwould print, such as the import of thesrc/models.tsa download wrote. It changes only thepropskey, through the writer the map editor uses, so a stroke written at the same moment stays. The whole map must parse before the file is written, and a refusal names the prop and the field, such asprops.tower.size. It prints where the engine stands each placed prop, then whatspawnite map checkfound, which now also lists a prop off the map and a model path that is no file in the game’spublic/folder; a registered model name is not checked.--assetsworks as it does forspawnite add asset.
The wiki
Section titled “The wiki”spawnite wiki listlists the wiki’s guide pages by folder: each one’s address forwiki read, its title, what it covers, and what a creator asks for that it answers, from the page’suseCases. The API reference, one page per export, is not listed;wiki searchfinds it.--jsongives each page’spage,title,descriptionanduseCasesunderpages.spawnite wiki search <query>finds the pages that answer the query, best first, at most 10, each with the address of the section that matched best and a line from it. A page titled exactly the query comes first, its guide page before its reference page.--scope guides,referenceorall, the default, says which pages to search.--jsongives each match’saddress,title,headingandlineundermatches.spawnite wiki read <page>prints one page’s markdown, or one section of it when the address ends in#anchor, aswiki searchgives them; the hosted wiki’s full address works too. A page or section past 60,000 characters reads in parts,--part 2for the second, and the reply says which part it is and how many there are. An unknown page refuses with the guide pages, an unknown anchor with the page’s sections, and--jsoncarriespage,title,heading,part,parts,markdownandsections.- The three are what the MCP’s
list_wiki,search_wikiandread_wikitools do, over the same code, so an agent without the MCP reads the wiki the same way.SPAWNITE_WIKIpoints them at another wiki, such as a folder holdingpages.json.
The playtest and the registry
Section titled “The playtest and the registry”The playtest the MCP server drives, a game’s own Vite dev server and a headless Chromium page on it, lives here too, as an export the server calls rather than a command.
The registry is the JSON the wiki serves under /r. Each game template and each feature is one item, and each item names the JSON Schema the wiki serves at /schema/registry-item.json, built from the registry’s shapes in @spawnite/schema. --registry points a command at another base URL, or at a folder of that JSON, and SPAWNITE_REGISTRY sets its default.