Skip to content

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 build

This writes the finished game to a dist/ folder:

  • dist/index.html is 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's assets/ 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 is index.html plus this assets/ 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 .ana file under passages/) and layouts (.layout files).
  • Your JavaScript in scripts/*.js, and every mod: its passages, its mod.meta, and its macros.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 to index.html with 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 preview

and 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/ (so index.html is 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 command npm run build and publish directory dist.

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.io is not visible when the same game runs from a downloaded file, or from your own domain, or from npm 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.