How to Distribute Your Games
This tutorial was written in February 2026, for v2 of the engine.
You've built a game — now what? This tutorial covers everything you need to know about exporting your Floaty game and getting it onto different platforms.
Exporting Your Game
Once you've got something you're happy with, you can export it as a standalone HTML file. In the playground editor toolbar, click the Export button (between Screenshot and Run) to download your game.
What You Get
The exported file is a single, self-contained HTML document. The engine code, your game code, and minimal CSS are all inlined — no external dependencies, no network requests needed to run it.
Here's what the structure looks like:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Game - Author</title>
<style>
html, body { margin: 0; padding: 0; width: 100%; height: 100%; overflow: hidden; }
.game { width: 100vw; height: 100vh; }
</style>
</head>
<body>
<div class="game"></div>
<script type="module">
// Engine code (inlined automatically)
// Your game code (inlined automatically)
</script>
</body>
</html>
You can open the exported file directly in any browser to play your game fullscreen. Double-click the file, or drag it into a browser window.
Note: If your game uses queue() to load remote assets, those URLs need to be accessible from wherever the file is opened. See the Asset Serving section below for details.
Asset Serving
When you're distributing your game outside of Floaty, how you handle sprites and sounds matters. There are three approaches, each with different trade-offs.
Inline Assets
Sprites defined as arrays and sounds defined as frequency arrays are embedded directly in your code. They don't need any network requests and work everywhere — in the browser, from file://, in Electron, on mobile, and on itch.io.
engine.scope(({ start, cls, spr, sfx }) => {
const sprites = {
player: [
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 7, 7, 7, 7, 0, 0,
0, 7, 7, 7, 7, 7, 7, 0,
7, 7, 0, 7, 7, 0, 7, 7,
7, 7, 7, 7, 7, 7, 7, 7,
0, 7, 7, 7, 7, 7, 7, 0,
0, 0, 7, 7, 7, 7, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
],
};
const sounds = {
jump: [523, 587, 659],
};
// No network needed — works from file://, itch.io, Steam, mobile
start({
target: document.querySelector('.game'),
sprites,
sounds,
draw() {
cls(1);
spr('player', 60, 60);
},
});
});
This is the simplest and most portable approach, especially for small games.
Remote Assets with queue() and load()
The queue() and load() functions (covered in the Loading Remote Assets section above) use fetch() internally to download assets. This works great when your game is served from a web server, but there's an important catch for exported games:
Important: The queue() function uses fetch() to download assets. This requires an HTTP or HTTPS URL. If you open an exported HTML file directly from your filesystem (the file:// protocol), fetch() will fail. Use inline assets, data URIs, or serve the file from a web server instead.
If you do use queue() in an exported game, make sure the URLs are absolute:
engine.scope(({ start, queue, load, cls, spr }) => {
const sprites = {
// Must be absolute HTTP/HTTPS URLs — not relative paths
tileset: queue('https://your-server.com/assets/tileset.json'),
};
const sounds = {
music: queue('https://your-server.com/assets/music.ogg'),
};
start({
target: document.querySelector('.game'),
sprites,
sounds,
async init() {
await load();
},
draw() {
cls(1);
spr('tileset', 0, 0);
},
});
});
Data URIs
Binary assets like audio files can be base64-encoded as data URIs. This gets rid of the network dependency but increases file size (base64 encoding adds roughly 33% overhead).
engine.scope(({ start, cls, sfx }) => {
const sounds = {
// Base64-encoded audio — no network needed
beep: 'data:audio/wav;base64,UklGRl9vT19...',
};
start({
target: document.querySelector('.game'),
sounds,
draw() {
cls(1);
// sfx('beep') works without any network access
},
});
});
Which Approach to Use
Use inline arrays for small games — sprites as pixel arrays and sounds as frequency arrays work everywhere with no setup. Use absolute HTTP URLs with queue() when your assets are hosted on a CDN or your own server. Use data URIs to embed binary assets (like audio files) directly in your code without needing a server.
Desktop with Electron
Electron packages web apps as native desktop applications for Windows, macOS, and Linux. Your exported Floaty game runs inside Chromium, so all web APIs work exactly as they do in a browser.
Project Setup
- Create a new directory for your desktop project
- Run
npm init -y - Run
npm install --save-dev electron - Create a
main.jsfile with the entry point shown below - Place your exported HTML file as
game.htmlin the project root - Update your
package.jsonto match the example shown below
Here's the main.js entry point:
const { app, BrowserWindow } = require('electron');
function createWindow() {
const win = new BrowserWindow({
width: 512,
height: 512,
useContentSize: true,
autoHideMenuBar: true,
});
win.loadFile('game.html');
}
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
app.quit();
});
And the package.json:
{
"name": "my-floaty-game",
"version": "1.0.0",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"devDependencies": {
"electron": "^35.0.0"
}
}
Running Locally
Run npx electron . from your project directory to launch the game in a desktop window.
Building for Distribution
To package your game as a standalone executable, use Electron Forge :
- Run
npm install --save-dev @electron/forge - Run
npx electron-forge packageto create a packaged app - Run
npx electron-forge maketo create platform-specific installers
Tips
- Electron uses Chromium, so
fetch()andqueue()work for remote assets — nofile://limitations - Set the window size to a multiple of 128 (e.g., 512x512) for clean pixel scaling
autoHideMenuBar: truehides the menu bar for a cleaner game experience
Mobile with Capacitor
Capacitor wraps web apps in a native mobile shell for iOS and Android. Your exported Floaty game runs inside a WebView.
Project Setup
- Create a new directory for your mobile project
- Run
npm init -y - Run
npm install @capacitor/core @capacitor/cli - Run
npx cap init - Place your exported HTML file as
www/index.html - Update your
capacitor.config.tsto match:
// capacitor.config.ts
const config = {
appId: 'com.example.mygame',
appName: 'My Floaty Game',
webDir: 'www',
server: {
androidScheme: 'https',
},
};
export default config;
- Add platforms:
npx cap add iosand/ornpx cap add android
Building and Running
- iOS: Run
npx cap open iosto open the project in Xcode (requires macOS) - Android: Run
npx cap open androidto open the project in Android Studio
From there, build and run using the IDE's standard tools.
Important: Mobile WebViews use capacitor:// (iOS) or https://localhost (Android) as the origin, not file://. This means fetch() and queue() may work for assets served from the www/ directory, but external URLs may need extra configuration. Test thoroughly on each platform.
Tips
- Inline assets are the most reliable approach for mobile
- Touch input maps to mouse events in Floaty (click, mousedown, etc.)
- Test on real devices — emulators may not accurately represent performance
- Consider locking screen orientation using Capacitor's
@capacitor/screen-orientationplugin
Publishing on itch.io
itch.io is the largest indie game marketplace. It's free to publish on and supports HTML5 games natively — your exported Floaty game can be played directly in the browser.
Preparing Your Game
- Export your game using the Export button in the playground editor
- Open the exported HTML file in a browser and verify it works
- If your game uses
queue(), make sure the URLs are absolute and publicly accessible
Uploading
- Create an account at itch.io
- Click "Dashboard" then "Create new project"
- Set "Kind of project" to "HTML"
- Upload your exported HTML file (or a zip containing it)
- Check "This file will be played in the browser"
- Set the viewport dimensions (recommended: a multiple of 128, e.g., 512x512)
- Save and publish
Tips
- Set the viewport to a multiple of 128 for clean pixel scaling
- itch.io serves games over HTTPS, so
queue()with absolute URLs works - If you need multiple files, zip them with the HTML file named
index.html - Test your game in the itch.io embed preview before publishing
Publishing on Steam
Steam doesn't natively run HTML5 games. To publish on Steam, wrap your game with Electron first (see the Desktop with Electron section above), then upload the packaged build.
High-Level Process
- Wrap your game using Electron and build it with Electron Forge
- Create a Steamworks account (requires a $100 app credit fee)
- Set up your app in the Steamworks dashboard
- Upload your Electron build using
steamcmd - Configure launch options to point to your Electron executable
- Test and publish
Note: Steam integration (achievements, leaderboards, etc.) requires the Steamworks SDK, which is a C++ library. Integrating it with an Electron app is possible via packages like steamworks.js but is beyond the scope of this guide.