Khalil Ghâñmî

Web Designer



Développement web

Khalil Ghâñmî

Web Designer



Développement web

Blog Post

How to Build a Multiplayer (.io) Web Game, Part 1

21 November 2020 web

A deep dive into the client-side Javascript of an .io game.

APRIL 25, 2019 | UPDATED JUNE 14, 2019

When came out in 2015, it inspired a new .io game genre that has since exploded in popularity. I experienced the rise of .io games firsthand: I’ve built and sold 2 .io games in the past 3 years.

In case you’ve never heard of .io games before: they’re free, multiplayer web games that are easy to join (no account required) and usually pit many players against each other in one arena. Other famous .io games include and

In this post, we’re going to understand how to build an .io game from scratch. All you need is a working knowledge of Javascript: you should be comfortable with things like ES6 syntax, the this keyword, and Promises. Even if you’re not the most familiar with Javascript, you should still be able to get through most of this post.

An Example .io Game

To help us learn, we’re going to be referencing the example .io game embedded below. Go ahead, try it out! You can play it right here on this page: mobile, it works best fullscreen at

It’s a pretty simple game: you control a ship in an arena with other players. Your ship automatically fires bullets, and you’re trying to hit other players with your bullets while avoiding theirs.

Table of Contents

This is Part 1 of a two-part series. Here’s what we’ll cover in this post:

  1. Project Overview / Structure: A high level view of the project.
  2. Builds / Project Setup: Development tooling, configuration, and setup.
  3. Client Entrypointsindex.html and index.js.
  4. Client Networking: Communicating with the server.
  5. Client Rendering: Downloading image assets + Rendering the game.
  6. Client Input: Letting users actually play the game.
  7. Client State: Processing game updates from the server.

We’ll go over the Server in Part 2.

1. Project Overview / Structure

I recommend downloading the source code for the example game so you can follow along.

Our example game uses:

Here’s what the project directory structure look like:



Anything in the public/ folder will be statically served by our server. public/assets/ contains images used by our project.


All the source code is in the src/ folder. client/ and server/ are pretty self explanatory, and shared/ contains a constants file that’s imported by both the client and the server.

2. Builds / Project Setup

As mentioned before, we’re using the Webpack module bundler to build our project. Let’s take a look at our Webpack configuration:

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  entry: {
    game: './src/client/index.js',  },
  output: {
    filename: '[name].[contenthash].js',    path: path.resolve(__dirname, 'dist'),  },
  module: {
    rules: [
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ['@babel/preset-env'],          },
        test: /\.css$/,
        use: [
            loader: MiniCssExtractPlugin.loader,
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',    }),
    new HtmlWebpackPlugin({
      filename: 'index.html',
      template: 'src/client/html/index.html',

A few key lines are highlighted above:

  • src/client/index.js is the Javascript (JS) client entrypoint. Webpack will start there and recursively look for other files that are imported.
  • The JS output of our Webpack build will be placed in the dist/ directory. I’ll refer to this file as our JS bundle.
  • We’re using Babel, specifically the @babel/preset-env config, to transpile our JS code for older browsers.
  • We’re using a plugin to extract all CSS referenced by our JS files and bundle it together. I’ll refer to this as our CSS bundle.

You may have noticed the strange '[name].[contenthash].ext' bundle filenames. They include Webpack filename substitutions[name] will be replaced with the entrypoint name (which is game), and [contenthash] will be replaced with a hash of the file contents. We do this to optimize for caching – we can tell browsers to cache our JS bundles forever, because if the bundle changes its filename will change, too (the contenthash changes). The final result is a filename like game.dbeee76e91a97d0c7207.js.

The webpack.common.js file is a base config file that we import in our development and production configurations. For example, here’s the development config:
const merge = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'development',

We use for efficiency while developing, and we switch to to optimize bundle sizes when deploying to production.

Local Setup

I recommend installing the project on your local machine so you can follow along with the rest of this post. Setup is simple: first, make sure you have Node and NPM installed. Then,

$ git clone
$ cd
$ npm install

and you’re ready to go! To run the development server, simply

$ npm run develop

and visit localhost:3000 in your web browser. The dev server will automatically rebuild the JS and CSS bundles when you edit code – just refresh to see your changes!

3. Client Entrypoints

Let’s get to the actual game code. To start, we need an index.html page, which is the first thing your browser loads when it visits a site. Ours will be pretty simple:


  An example .io game


This code sample is slightly abridged for clarity, as many code samples in this post will be. You can always see the full code on Github.

We have:

  • An HTML5 Canvas () element that we’ll use to render the game.
  •  include for our CSS bundle.