Skip to content

Publishing to the Web

JoeStrout edited this page Oct 23, 2026 · 2 revisions

raylib-miniscript runs in a web browser, too. This page explains how to build the web version of your game, test it, and put it on a web server, and how to bundle your game's files into a file package so the page loads faster.

Building

You need the Emscripten SDK, either installed in the project root as emsdk/ or linked there. Then, from the project root:

scripts/build-web.sh

This produces a build-web folder containing:

File What it is
raylib-miniscript.html the page that hosts the game
raylib-miniscript.js, raylib-miniscript.wasm the engine
index.html a redirect to raylib-miniscript.html, so the folder's plain URL works
assets/ a link to your project's assets folder (main.ms, imported modules, images, sounds, and so on), not a copy, so the page always sees your latest files

Testing locally

Browsers won't run the page straight from your disk (a file:// URL); it has to come from a web server. Python's built-in one is fine for testing:

cd build-web
python3 -m http.server 8000

Then open http://localhost:8000. Leave the server running while you work; after a rebuild, or after editing anything in assets, just reload the page.

Two ways to deliver your files

The engine always starts by running assets/main.ms. There are two ways to get that file, and everything else your game reads, into the browser.

Loose files (the default)

The page fetches each file from the server when the game first asks for it: the main script, each import, and each image, sound, font or shader. To publish, upload the whole build-web folder, including the files in assets/. Since build-web/assets is a link, copy the folder in a way that follows it, such as cp -RL build-web mygame or rsync -aL build-web/ server:mygame/; plain cp -R, rsync -a and most zip tools copy only the link itself, which won't work on the server.

That's one network request per file. import is the costly part: it looks for a module in each folder on the import path in turn (by default, the main script's folder and then its lib folder), and each place it isn't found is a wasted request that returns "404 Not Found". A game with dozens of modules can make hundreds of requests before its title screen appears. Each request is fast on your own machine, but over the internet they add up.

A file package

A file package bundles your entire assets folder into a single file, game.data, along with a small loader script, game.data.js. When the page opens, the loader downloads game.data in one request and unpacks it into the browser's memory, before your game starts. From then on, every file your game reads comes straight from memory, with no network requests at all.

(The package is an Emscripten feature. The in-memory "disk" it unpacks into is the one Emscripten gives every web program in place of a real file system.)

Which should I use?

Loose files File package
Requests to start the game one per file, plus misses for every import one
Files download when first needed all of them, before the game starts
Best for a few files; large media the game might never use many modules or small assets; anything you're publishing
After changing a file just reload re-run scripts/package-web.sh, then reload

Most finished games are better off with a package. The main exception is a game with a lot of large media, such as long music tracks or video, which would make players wait at a blank screen while it all downloads. With a package there's no way to leave those files out and fetch them later (see the rules below).

Loose files are often handier while you're still developing, since an edit to a script takes effect as soon as you reload.

Making a package

After scripts/build-web.sh, run:

scripts/package-web.sh

This:

  1. packages your assets folder into build-web/game.data and build-web/game.data.js (skipping hidden files such as .DS_Store);
  2. adds the line <script src="game.data.js"></script> to build-web/raylib-miniscript.html, just before the engine's script tag.

To package a different folder, or write the package somewhere else, give their paths:

scripts/package-web.sh path/to/mygame/assets path/to/output

Whatever the folder is called on your disk, the game sees its contents as assets/.

Run it again after every build-web.sh: the build rewrites the page, which removes the loader line. (Running it twice is harmless; it won't add the line again.) Run it after you change any game file, too, since the package is a snapshot.

To publish, upload raylib-miniscript.html, raylib-miniscript.js, raylib-miniscript.wasm, game.data, game.data.js, and (if you want the short URL) index.html. The assets/ folder is not needed.

Using your own page

If you host the game in a page of your own rather than the one the build generates, add the loader yourself. It must come after the script that defines var Module, and before the engine script:

<script>
  var Module = { /* ... */ };
</script>
<script src="game.data.js"></script>
<script async src="raylib-miniscript.js"></script>

The rules, with a package

  • All or nothing. Once a package is loaded, the engine never fetches a file over the network. A file that isn't in the package doesn't exist, as far as the game is concerned: import reports "library not found", LoadTexture and friends fail as they would for any missing file, and FileExists returns 0. You can't package some files and leave the rest loose.
  • It has to be your assets folder. The engine decides whether a package is loaded with one check, made once at startup: is there an assets folder in memory? package-web.sh always unpacks to assets. If you run Emscripten's packager yourself, use --preload yourfolder@/assets; a package unpacked anywhere else is ignored, and files are fetched as usual.
  • import searches the same folders as without a package, just in memory, so your import paths don't change.
  • FileExists and DirectoryExists see the package. Without a package they can only see files your game has created during this session (next rule), since they can't ask the server.
  • Saved files vanish on reload. ExportImage, TakeScreenshot and similar functions write into the same in-memory file system, whether or not you use a package. Your game can load those files back during the session, but they disappear when the page is reloaded, and the player can't download them.

Troubleshooting

  • The page says "Not found in the file package: assets/main.ms". A package loaded, but it has no main.ms at its top level. Check that you packaged the right folder: main.ms must be directly inside it, not in a subfolder.
  • The game never starts; the canvas just stays black. The loader probably couldn't download game.data. Open the browser's developer console (usually F12, or Cmd-Option-J on a Mac) and look for an error naming game.data; most likely it isn't next to the page on the server.
  • Your changes don't show up. Re-run scripts/package-web.sh, then reload. If the old version still appears, the browser is probably showing a cached copy of game.data; do a hard reload (Shift-Reload, or Cmd-Shift-R on a Mac).
  • A module or asset is "not found", but works on desktop. With a package, the file has to be inside the packaged folder. The desktop build can reach files outside it (for example, through a /usr disk you've mounted); the web build can't.

Clone this wiki locally