A container ship

Finish and publish your HTML5 game

Learn how to publish your HTML5 game on the web, on game portals, in the app stores and on Steam. But first, look back at the bee game you've built in this tutorial series and play a more polished version of it, complete with source code.

Here's Pollen Rush, the bee game with a few extras. Collect the yellow pollen and stay away from the other bees. Click or tap the canvas to start:

Your own bee has a dashed ring around it. Steer with the arrow keys or WASD, or press and hold where you want to fly, with the mouse or your finger. Every bee that hits you costs one of your three lives. Collect pollen quickly, one grain after another, for a combo, and grab the golden pollen before it disappears. After every 10 grains, another bee joins the swarm.

Look back at what you've built

Pollen Rush is built from the same pieces as the game you made in this series. The series started with an overview of how to make an HTML5 game. Then every part added one piece:

  • The canvas - Everything in the game is drawn on one canvas element, sharp on every screen. You learned how to set up an HTML5 canvas.
  • Drawing - The pollen, the score and the buttons are shapes and text that you draw on the canvas with JavaScript.
  • The game loop - Every frame, the game updates everything and draws it again. That's the JavaScript game loop.
  • Animation - Some screens draw 60 frames per second, others 144 or more. So every bee moves a fixed number of pixels per second, not per frame, and the game runs at the same speed on every screen. That's how you make a smooth canvas animation.
  • Collisions - A circle around every bee and every grain of pollen tells the game what touches what. The bees bounce off each other with the physics from collision detection.
  • Sprites - The bees flap their wings with a sprite animation, and get dizzy when they bump into something.
  • Controls - You can steer your bee with the arrow keys or WASD, the mouse or a finger. Learn how to add keyboard, mouse and touch controls.
  • Game states - The start screen, the game itself, the pause menu and the game over screen are JavaScript game states.
  • Sound - Every pickup, bump and hit has its own sound effect, and the music gets softer in the pause menu.

Want to see each part at work? The canvas below plays Pollen Rush by itself. Pick a part with the X-ray buttons, and the canvas shows what it does in the game:

X-ray

With Collisions, for example, you see the circles that the game checks every frame. When two bees touch, their circles fill with red. With States, you see the game go from the start screen to playing, to game over and back.

What's new in Pollen Rush

Pollen Rush starts from the complete code of the part about sound effects and music, and gives the game a goal. The extras are built almost entirely from pieces you already know:

  • Pollen - There are always four yellow dots on the field, each one a grain of pollen. A grain is an object with a position, a radius and an age. The game uses circleIntersect(), the function that checks whether two circles overlap, to see if your bee touches a grain. The game uses the age to make a new grain grow to full size, and to make a golden grain blink and disappear. Collect a grain, and a new one grows somewhere else, never right next to your bee.
  • Combo - Collect the next grain within 2 seconds, and it's worth twice as much, then three times, up to five times. A bar under the score shows how much time you have left. A hit ends your combo.
  • Golden pollen - Every 10 to 20 seconds, a bigger, golden grain appears. It's worth 50 points, but it starts to blink after 3 seconds and disappears after 5.
  • Waves - After every 10 grains, a new wave starts. One more bee joins the swarm, a little faster than the others. A red ring warns you a second before the new bee appears.
  • Screen shake - When a bee hits you, everything on the field shakes for a moment.

The screen shake is the smallest extra, and you can add it to any game. Before you draw, move the canvas a few pixels in a random direction, and let the shake fade out:

// When a bee hits you, in hitPlayer(): full strength
shake = 1;

// In draw(): move everything a few pixels in a random direction
context.save();
if (shake > 0) {
    let size = 8 * shake;
    context.translate(
        (Math.random() * 2 - 1) * size,
        (Math.random() * 2 - 1) * size
    );
}
// ...draw the game objects here...
context.restore();

// Every frame: fade out in a third of a second
shake = Math.max(shake - 3 * secondsPassed, 0);

shake is 1 right after a hit, and 0 when everything stands still. save() remembers how the canvas was set up, and translate() moves everything you draw after it. restore() puts the canvas back the way it was, so the score and the buttons you draw after it don't shake. Small effects like this, which make a game feel alive, are called game juice. The points that float up when you collect pollen are another example. You'll find all these extras in the complete code at the end of this page.

Get your game ready for release

A game that works on your own computer isn't finished yet. Before you share it, go through this list:

  • Test on a real phone - The mobile view in your browser's developer tools doesn't show how fast your game runs on a phone, or how it feels to play with your thumbs. Check the controls, the sound and the screen size.
  • Keep it small - Players leave when a game takes too long to load, and game portals have size limits. Make your images as small as you can, and use MP3 files for sound.
  • Make it easy to start - Tell players what to do on the start screen, like "Collect the pollen. Dodge the bees.", and let the first click or tap start the game.
  • Pause when the player leaves - Pause the game when the player switches tabs or clicks outside the game, with the visibilitychange and blur events, as shown in the part about game states.
  • Credit what you use - Free images, sounds and music often come with a license that asks you to credit the creator. Put the names in your game or on your game's web page.
  • Write a privacy policy - App stores ask for one. Say what your game saves and what it sends, even if that's nothing more than a high score in local storage.
  • Prepare an icon and screenshots - Every store and portal asks for them, along with a short description. A short video or GIF of your game helps people decide to play it.

Put your game on itch.io

itch.io is the easiest place to start. It's free, and you can upload an HTML5 game in a few minutes:

  • Step 1 - Put your game in a folder, and name its HTML file index.html. Load your images and sounds with relative paths, like img/bee.png, not /img/bee.png. itch.io runs your game from a folder of its own, and a path that starts with a slash looks in the wrong place.
  • Step 2 - Zip the folder. A game that's just one HTML file can also go up on its own, without a zip.
  • Step 3 - Create a new project on itch.io, mark it as an HTML game and upload the zip.
  • Step 4 - Choose how your game shows up: embedded on the page at a size you pick, or in full screen after the player clicks. itch.io adds a full-screen button for you. If your game supports touch, turn on Mobile Friendly.

itch.io doesn't take a fixed cut. You decide how much of every payment goes to itch.io, and it's 10% unless you change it. For a game that runs in the browser, itch.io only lets you ask for donations. To sell your game, offer it as a download.

Want your game on a site of your own instead? The tutorial about how to make an HTML5 game shows a few other places to put it, like GitHub Pages.

Publish on game portals like Poki and CrazyGames

Game portals have millions of players, and they share their ad revenue with you. But they're picky:

  • Poki - Every game on Poki is hand-picked. You apply with a link to a playable version, or with a video. Your game shows ads through the Poki SDK, a script from Poki that you add to your game. What the player loads before the game starts should be 5 MB at most, and the whole game 8 MB at most. Poki usually asks for web exclusivity for a few years. That means your game can't be on other game sites, and maybe not on itch.io or your own site either, so read the deal before you sign. The app stores and Steam stay open to you.
  • CrazyGames - Anyone can submit a game. Your game starts with a basic launch, without ads. After at least a week and 500 plays, CrazyGames decides whether it gets a full launch, with ads through their SDK. Your whole game can be up to 50 MB, or 20 MB if you want it to show up on the CrazyGames homepage on phones. With their SDK, your game can be up to 250 MB, and those limits only apply to what the player loads before the game starts. The rest can load while they play.

Portals turn down most games, so don't take a no personally. Improve your game and try again, or try a different portal.

Make your game installable as a web app

Before you look at the app stores, there's an easier way to get your game onto a phone's home screen. Turn it into a progressive web app, or PWA. The browser can then install your game like an app. It gets its own icon, and it opens without the address bar.

Your game needs to be on a site with HTTPS, like GitHub Pages, and it needs a manifest. For testing, a local server works too, but opening the HTML file by double-clicking it doesn't. The manifest is a small file with the name of your game, its icons and how it should open:

{
    "name": "Pollen Rush",
    "short_name": "Pollen Rush",
    "start_url": "./",
    "display": "fullscreen",
    "background_color": "#ffffff",
    "icons": [
        {
            "src": "icon-192.png",
            "sizes": "192x192",
            "type": "image/png"
        },
        {
            "src": "icon-512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ]
}

Make the two icons it lists, 192 and 512 pixels square. start_url is the page that opens, and with display set to fullscreen, your game opens without any browser bars. background_color is the color of the screen while your game loads.

Save it as manifest.json in the same folder as your index.html, and link to it in the <head> of your game:

<link rel="manifest" href="manifest.json">

To check it, open your game in Chrome and open the developer tools. Under Application, then Manifest, Chrome lists anything that's still missing. Once it's all there, an install button shows up in the address bar.

Want your game to work without an internet connection, too? Then add a service worker. That's a script that saves your game's files in the browser and serves them from there, so the game also works without a connection:

// sw.js: your game's files, saved under a version name
const cacheName = 'pollen-rush-v1';
const files = [
    './',
    'index.html',
    'img/bee-sprite-sheet.png',
    'sound/bee-music.mp3'
];

// Save all files when the service worker is installed
self.addEventListener('install', function (event) {
    event.waitUntil(
        caches.open(cacheName).then(function (cache) {
            return cache.addAll(files);
        })
    );
});

// Remove the files of older versions of your game
self.addEventListener('activate', function (event) {
    event.waitUntil(
        caches.keys().then(function (names) {
            let old = names.filter(function (name) {
                return name.startsWith('pollen-rush-') &&
                    name != cacheName;
            });
            return Promise.all(old.map(function (name) {
                return caches.delete(name);
            }));
        })
    );
});

// Answer with a saved file, or else ask the network
self.addEventListener('fetch', function (event) {
    event.respondWith(
        caches.match(event.request).then(function (response) {
            return response || fetch(event.request);
        })
    );
});

'./' is the folder itself, the address that players usually open. Save the script as sw.js in the same folder as your index.html, and register it in init(), the function that starts your game when the page has loaded:

if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('sw.js');
}

Add every image and sound your game uses to the files array. They have to be your own files, in your game's folder, not files on another site. The complete code at the end of this page loads its images and sounds from spicyyoghurt.com, so replace them with your own files first. And whenever you change a file, change the version in cacheName too. Then the browser fetches the new files on the next visit, and throws away the old ones.

Publish your game on Google Play

Google Play accepts a web app as an Android app, through a Trusted Web Activity. Your game stays on your website, and the app opens it in full screen. Bubblewrap, a tool from Google that you run in a terminal, builds the app from your manifest. Like Capacitor and Electron further down this page, it needs Node.js, which comes with the npm command:

npm install -g @bubblewrap/cli
bubblewrap init --manifest=https://example.com/game/manifest.json
bubblewrap build

bubblewrap init asks a few questions, and can install the tools it needs by itself. bubblewrap build then makes an app bundle, an .aab file, that you upload to Google Play. Would you rather click than type? PWABuilder does the same in your browser.

The app only opens your game in full screen if your website proves that the app is yours. Put a file named assetlinks.json in a folder named .well-known at the root of your site. It holds the fingerprint of the key your app is signed with. Google Play signs your app with its own key, so put the fingerprint of that key in the file. You'll find it in the Play Console, under App integrity. Bubblewrap can write the file for you, with the fingerprint you give it.

A developer account on Google Play costs a one-time fee of $25. Is it a new personal account, not a company account? Then Google wants at least 12 testers to try your game for 14 days in a row before you can publish it for everyone. So ask your friends early.

Publish your game on the App Store

Apple doesn't accept an app that only opens a website. Your game has to be inside the app. Capacitor puts your HTML, JavaScript, images and sounds into an iPhone app. Put your game in a folder named www, and run this in the folder that contains it:

npm install @capacitor/core
npm install -D @capacitor/cli
npx cap init
npm install @capacitor/ios
npx cap add ios
npx cap open ios

npx cap init asks for the name of your app, an ID like com.example.pollenrush and the folder with your game: www. npx cap open ios opens the project in Xcode, Apple's app for making apps. From there, you test the game on your iPhone and submit it to the App Store. Changed your game? Run npx cap copy ios to copy the new files into the app.

You need a Mac with Xcode, and an Apple Developer Program membership, which costs $99 a year. Apple also checks that every app is more than a website in a box. A game that works offline, with all its files inside the app, has a head start. Make sure it doesn't send players to a website, and that all of it works offline.

One more thing: an iPhone's silent mode also silences the Web Audio API. If you want your game to play sound in silent mode too, set navigator.audioSession.type to 'playback', as explained in the part about sound effects and music.

Publish your game on Steam

Steam sells games for Windows, Mac and Linux, and an HTML5 game can be one of them.

To turn your game into a desktop program, wrap it in Electron. Electron opens your index.html in a window of its own. Install Electron with npm install electron --save-dev. Then put this main.js in the same folder as your index.html. It opens a 750 by 400 window, the size of the canvas, with your game in it:

const {app, BrowserWindow} = require('electron/main');

function createWindow() {
    let win = new BrowserWindow({width: 750, height: 400});
    win.loadFile('index.html');
}

app.whenReady().then(createWindow);

// Quit when the window closes, except on a Mac
app.on('window-all-closed', function () {
    if (process.platform != 'darwin') {
        app.quit();
    }
});

npx electron . starts your game in its window. To turn it into a program you can upload to Steam, add Electron Forge to your project, as its guide shows, and run npm run make. Each operating system needs its own build, usually made on that system: the Windows version on Windows, and the Mac version on a Mac. For achievements and the Steam overlay, steamworks.js connects your game to Steam. It's not an official Valve tool; it's a free project by other developers.

Steam charges $100 per game. You don't get it back right away, but once your game has earned $1,000, Steam adds it to your payout. After you pay, it takes at least 21 days before you can release your game, and your store page has to say Coming Soon for at least two weeks before the release.

Which platform should you start with?

Start small. Put your game on itch.io, and share the link with your friends. Watch them play, and fix what they get stuck on. Does it play well on a phone? Then try a game portal. The app stores and Steam take more time and money, so save them for a game that people already like to play.

Common questions about publishing your game

How much does it cost to publish a game?

Nothing on itch.io, GitHub Pages or a game portal. A developer account on Google Play costs a one-time fee of $25. The App Store costs $99 a year. Steam charges $100 per game, and adds it to your payout once your game has earned $1,000.

Do I need a Mac to publish on the App Store?

Yes. Xcode, the app that builds iPhone apps, only runs on a Mac. Don't have one? You can rent a Mac in the cloud for as long as you need it.

Do I need to rewrite my game for phones?

No. If your game works with touch and fits the screen, it runs in the browser on a phone, and the app stores get the same code. You set that up in the parts about the canvas and the controls.

Can I publish the same game in more than one place?

Yes. itch.io, the app stores and Steam are fine with that. A game portal like Poki may ask for web exclusivity, but that still leaves the app stores and Steam open to you.

Can I use the code, images and sounds from this series in my own game?

You can use the code, change it, and even sell a game that uses it, as long as you credit Spicy Yoghurt, for example in a comment in your code. That doesn't include the bee images and the sounds, so make your own, or use free ones with a license that allows it. The details are in our terms.

The complete Pollen Rush code

Here's the complete code of Pollen Rush. It builds on the code from the part about sound effects and music, and it loads the sprite sheet and the sounds from spicyyoghurt.com. Paste it into an HTML file and open it in your browser:

The story behind this series

This series has been a long time in the making. The first three tutorials came out in March 2019. Over the next two years, four more followed, about the canvas, drawing, sprites and collisions. In 2026, we went over every part again, updated the code and the demos, and wrote the last four parts to finish the series. That's more than seven years, and a lot of evenings and weekends.

These tutorials are free to read, but the hosting isn't, and we pay for it ourselves. So if this series helped you, we'd really appreciate a donation.

What's next?

You've reached the end of the series. You started with an empty canvas, and now you have a game with sprites, controls, game states and sound, and you know how to get it out into the world.

Where do you go from here? Make Pollen Rush your own, with a new kind of enemy, a power-up or levels. Or try something new: make a flock of birds fly together, or use random numbers to make every round a little different.

If you have any questions, feel free to ask them in the comment section. And share your best Pollen Rush score while you're there! You can

If you'd like to support Spicy Yoghurt, please share our articles or follow us on social media.