# Putting an exported game in a web page Take a game made with the Game Kits out of GripForge and serve it yourself: what `gripforge game-export` writes, how to serve it, what the page accepts, and what it says when it cannot start. Readable offline: `grip-forge docs web-export`, or the MCP tool `gripforge_docs` (topic `web-export`). ## What it is for A web export is a folder of static files: the page, the player, the logic of the game's kits and its assets. It plays without GripForge's servers and without a login. You host it as any static site. ## Prerequisites - The GripForge CLI, logged in, with access to the workspace that owns the project (also for a public game). - Node.js 22.19 or later, for the CLI and for the local launcher of the export. - A Game Kit project id (`gkp_…`). ## Install and first run ```sh gripforge game-export gkp_... ./my-game cd my-game npm run dev # Game ready: http://127.0.0.1:5173 npm run dev -- --port 5174 # another port ``` No dependency is installed: `npm run dev` runs `server.mjs`, a small static server that ships in the folder. ## What the folder holds | Path | What | |---|---| | `index.html` | The page: one `
` and the player's module. | | `game.json` | The project: its configuration and content. This is the file you edit. | | `player/` | The compiled page player (`player.js`, `player.css`, its workers) and the web views of the game's kits only. | | `runtime-rust/` | When the game is drawn by the GripForge player: the player (WebGPU and WebGL2 builds), the presentation modules of the game's kits, and `kit-modules/` — the logic of the game's kits, one WebAssembly module per kit plus the core. | | `assets/` | The models, textures and sounds of the game. | | `third-party-kits/` | The kits of your own the game declares ([Writing a kit](../kit-third-party.md)). | | `server.mjs`, `package.json`, `README.md` | The local launcher (`npm run dev`). | | `.gripforge/web.lock.json` | The kit versions and the hash of every delivered file. | ## Serving it from your own site The page of the export is complete: publish the folder as it is, under any path. `index.html` finds every file relative to its own address, so nothing has to be at the root of the site. What your server must do: | Requirement | Why | |---|---| | Answer `.wasm` as `application/wasm` | The browser refuses to compile WebAssembly served as anything else. | | Answer `.js` and `.mjs` as `text/javascript` | Module scripts are refused with another type. | | Serve every file of the folder, with the same relative paths | The page loads `./game.json`, `./player/…`, `./runtime-rust/…`, `./assets/…`. | `server.mjs` does exactly this (GET and HEAD only, `cache-control: no-store`, a 404 for anything outside the folder): read it as the reference. ## Page options Added to the address of the page: | Option | Values | Effect | |---|---|---| | `?logic=` | `wasm` (default), `kits` (the same), `ts` | Which logic runs: the Rust logic of the kits (one module per kit), or the TypeScript kit host. Without it, `logic` of `game.json` decides, then `wasm`. A game with a third-party kit does not play it under `ts`: the page says so in a red line. | | `?renderer=` | `bevy`, `three` | Draws with the other renderer, to compare or diagnose. The page shows no control for it. | ## Updating an export ```sh gripforge game-export gkp_... ./my-game --check # what would change, nothing written gripforge game-export gkp_... ./my-game # apply gripforge game-export gkp_... ./my-game --force # back up local edits of managed files, then replace them ``` Nothing updates silently. Files you added are never touched. If a delivered file was edited locally, the whole update stops and lists the conflicts; `--force` backs them up in `.gripforge/web-backups/` and replaces them. `--json` prints the report (`added`, `updated`, `removed`, `preserved`, `conflicts`, `budget`). After an export the CLI checks the models of `assets/` against the render budget and prints warnings; a warning never refuses an export (`grip-forge docs render-budget`, or `gripforge game-budget`). ## Errors ### From the CLI | Message | Cause | |---|---| | `Expected a Game Kit project id (gkp_…).` | The first argument is not a project id. | | `Usage: gripforge game-export [folder] [--check] [--force] [--json]` | An unknown option or a missing argument. | | `Incomplete export: ` | The server's export lacks a required file (`index.html`, `game.json`, `package.json`, `server.mjs`, `player/player.js`). | | `Integrity check failed: ` | A downloaded file is not the one the export names (size or SHA-256). Nothing is installed. | | `Export download failed for (HTTP ).` | A file could not be downloaded. Nothing is installed. | | `This directory belongs to a different export. Choose another directory.` | The folder holds the export of another project or of another server. | | `Another export is active. …` | `.gripforge/export.lock` exists: another export is running, or one was interrupted. | | `File changed during export: ` | A file was edited while the update was being applied. | Files are installed only after every download succeeded. ### From the page The page never starts a game it could only play wrong: it stops and says why. | What the page says | Cause | |---|---| | `Unable to start the game. Cannot load game.json ()` | `game.json` is not served next to `index.html`. | | `The game player is missing on this server. …` | The files of `runtime-rust/` are not served, or not all of them. | | `This browser cannot draw the game: neither WebGPU nor WebGL2 answers. …` | No graphics acceleration in this browser. | | `This game page and the installed player are not of matching versions. …` | The page and the player do not speak the same protocol version: export the game again. | | `The game player could not start. …` | The player was there and the browser could draw, and it still failed: reload, then look at the console. | | `kit-modules/kit-modules.json: the manifest of the kit modules was not read (HTTP 404)` | `runtime-rust/kit-modules/` is not served. | | `@: no module in the kits package` | The kit modules served do not hold a kit of the game: export again. | | `: … its module has sha256 …, the game declares …` | The module served is not the one the game declares: export again. | | `: the kit module is version , the game declares ` | `kit-modules.json` and `game.json` come from two different exports. | The player draws with WebGPU and falls back to WebGL2 by itself; it never falls back to another renderer in silence. The console line `[gripforge] kit logic modules: the core and kits` tells which logic modules were loaded. ## Known limits - The export does not include GripForge's backend or generation services. - Kits that need online services (`net.*`, `fps.network`) still need them; the CLI lists them after the export (`Online services required by: …`). An export with `fps.network` ships `fps-server.mjs` (`npm run server`) for private rooms. - Exporting does not publish the project or change its visibility. - Legacy kits and unpublished source overlays are refused by the export. - An update from the site knows GripForge's kits only: after it, run `gripforge kit verify .` on a game with a kit of your own, and add the kit again if it no longer verifies. ## Versions The export manifest is `gripforge.web-export/1`; the lock of the folder is `gripforge.web-lock/1`. The kit versions delivered are in `.gripforge/web.lock.json`. `gripforge game-export` exists since CLI 0.3.15.