Appearance
Building & Distributing Your Game
When your game is ready to share, you build it once into a single self-contained HTML file. There is no server to run and no database to set up. The build inlines the engine, your passages, your styles, and everything else into one index.html that a player can open straight from their browser, the same way Twine and other HTML games ship.
Build it
bash
npm run buildThis writes the finished game to a dist/ folder:
dist/index.htmlis the whole game. The engine code, all your passages and layouts, the themes, and the CSS are inlined into this one file. If your game uses no images or audio, this single file is your game, and you can hand it to anyone.dist/assets/holds your images (assets/images/), video (assets/video/), and audio (assets/audio/), copied straight from your project'sassets/folder. They stay as separate files next to the HTML so the file doesn't balloon, and an image referenced as(img: "bartender/neutral")keeps working. If you use assets, the game isindex.htmlplus thisassets/folder.
You can delete and rebuild dist/ at any time. It is generated output and is already in .gitignore, so you never commit it.
Why a single file? Browsers block external script files loaded over
file://for security, so a normal multi-file build will not run when a player double-clicks it. Inlining everything into one HTML sidesteps that: there is nothing external to fetch, so the page just runs. It also means each build is one portable artifact. A mod author who rebuilds with their mod produces a different single HTML file to hand out.
What ends up in the file
Everything the engine loads at build time is compiled into index.html:
- Your passages (every
.anafile underpassages/) and layouts (.layoutfiles). - Your JavaScript in
scripts/*.js, and every mod: its passages, itsmod.meta, and itsmacros.js. Mods are bundled at build time, which is exactly why a modder rebuilds the game to produce their own version to share. - CSS that is linked in
index.html(the engine styles,layouts.css,game.css, and your theme stylesheets) is inlined automatically. Any custom CSS, including a new theme or a stylesheet for a custom layout, must be added toindex.htmlwith a<link>to be inlined. Anything you generate at runtime with the(css:)macro is injected as an inline<style>and needs nothing special.
The one thing kept outside the HTML is your assets/ folder (images and audio), copied next to index.html so the file stays a reasonable size. They load fine from a downloaded copy because they sit right beside the page.
Check it locally
The simplest test is the real thing: double-click dist/index.html (or open it in your browser). Because everything is inlined, it runs from your file system with no server.
To test it the way a web host will serve it (over http), run:
bash
npm run previewand open the URL it prints. This is worth doing before you publish to a site, since a hosted origin is where save data behaves most predictably (see below).
If the page is unstyled or shows only a "rotate your device" message on desktop, the build did not finish. Rebuild and try again.
Ship it
You can distribute the game two ways, and the single-file build supports both.
As a download
Zip dist/index.html together with the assets/ folder (or just the HTML if you have no assets) and hand it out. The player unzips it and opens index.html in their browser. This is the moddable, offline-friendly form: the file is theirs, and a mod author can ship a rebuilt version the same way.
On a web host
Upload the contents of dist/ to any static host:
- itch.io. Zip the contents of
dist/(soindex.htmlis at the top level of the zip). On your project page set the kind to HTML, upload the zip, and tick This file will be played in the browser. itch.io can also offer it as a download. The build uses relative paths, so it runs from itch.io's sandboxed subpath with no extra setup. - GitHub Pages. Push the contents of
dist/to the branch Pages serves. Relative paths handle project sites served from a/your-repo/subpath. - Netlify. Drag the
dist/folder onto the Netlify drop zone, or connect the repo with build commandnpm run buildand publish directorydist.
Where save data lives
Player saves live in the browser's IndexedDB, which works both from a web host and from a file opened locally. IndexedDB is scoped to the origin, which means the saves belong to the exact place the game runs from. A few things worth telling your players, and worth remembering while testing:
- Saves do not follow the game between origins. A save made on
itch.iois not visible when the same game runs from a downloaded file, or from your own domain, or fromnpm run preview. Each one is a separate origin with its own slots. - Clearing browser data wipes saves. A browser's "clear site data" for that origin removes the save slots and the autosave. That is the browser doing its job, not a bug in the game.
Saves are validated and migrated on load, so a save from an older version of your game still loads after you ship an update, as long as it stays on the same origin.
Next steps
If you have not set up your own game yet, the Quick Start covers the project layout and where your content lives. For the macros that read and write save slots, see Config, Save, Keybinds & Passage Utilities.