TBF #16 – My Three Weeks Of Hell: Porting To The Web

This is a three-part series about how we ported our custom C++ engine to the web for the prototype build. These devlogs are way more technical than our usual blog posts, as we think they might help other developers doing the same body of work.

Welcome…

Hello everyone!

As you know, we shipped our prototype build on the web. If, for any reason, this is the first time you're learning about it, you can play it right here, right now!

I want to take this opportunity to discuss how complicated it was to port the game to the web, and the tribulations and issues I faced as a programmer to get our engine to work. As a result, these blog posts will be longer and much more technical than usual. Each blog post will tackle a different subject: this one will discuss the plan for porting, the second will be about Emscripten compilation to WASM, and finally, the third will be about WebGPU.

If you're interested in this subject, I'm sure you'll learn a thing or two along the way.

It's also an opportunity for me to pour out all the emotions I collected over three weeks of porting the game to the web. It was complicated and hard, and sometimes I simply wanted to quit. But we did manage to get a functioning result in the end, so it was worth it.

Without further ado, let's dive in.

… To Hell

It all started with a fact: it's simpler to get people playing a build on the web than to force them to install a random executable on their computer. In fact, on Itch.io, it's about ten times more likely to happen. Having the capability to offer a web version of our games was something we'd wanted to do for the longest time, but the opportunity never arose until now. This meant we needed to support the web as a valid target for our engine.

I started the process of porting the game on May 1st, with the goal of shipping the product by May 22nd to be in time for "La Caravane," which happened from the 22nd to the 24th of May. The deadline was set; the goal was simple: get the game playable on the web and, if possible, playable on mobile. It would be way easier to carry a phone to let people play than a full laptop. I make the distinction now, because as you'll see throughout this series, it's an important one. Even if it's counterintuitive, you can consider each browser on each piece of hardware a specific platform to support. You can't expect that because it works in one browser, on one platform, it'll work everywhere, or even in the same browser on different platforms.

I'll start by explaining the structure of the engine we use and the challenges I would face while doing the porting. It should give you enough context to understand the different decisions that I made.

At Cellier, I developed a custom game engine for our needs, which comes with its own set of benefits and challenges. The main reason for that was the ability to develop faster once the engine reached maturity. We can boot the editor in under a second, hot reload any kind of asset we have (materials, VFX, sounds, textures, etc.), and produce content exactly as we envisioned it. The engine is also lightweight, so it can be booted on a laptop without any trouble.

It hasn't always been easy, but as I continue to work on it, it gets better and better. As of May 1st, the engine, editor, and game code were around ~100K LOC, which we needed to port to the web. I knew at the time that we needed to upgrade the core libraries we use in the engine.

Here was the list that needed to be upgraded:

  • SDL2 → SDL3: we knew SDL3 had a lot of improvements over their codebase. They also added support for platforms like the web through their SDL3 main events system (SDL_MAIN_USE_CALLBACKS). We'll discuss it eventually in the second part.
  • FMOD 2.0.2 → FMOD 2.03. We use FMOD as our sound engine, and version 2.02 wasn't totally compatible with the approach we wanted to use. We initially wanted to use threading for audio on the web, but it turned out not to be needed in the end.
  • I also took the opportunity to upgrade Vulkan (1.2 → 1.4) and Metal (3 → 4) for the future, since I was already deep into upgrading our core libraries.
Rendering Is Hard

One of the main challenges I have is drawing to the screen. Each platform uses a different graphics API. We support DX12, Metal, and Vulkan under a library I call internally SGL. This library is the core of our rendering logic. This is commonly called RHI, which is a Rendering Hardware Interface, and it makes it agnostic to the engine/game what's happening under the hood on the graphics side. As a result, the game never considers the real API it's using. It just asks to draw elements onto the screen, which are "magically" drawn at some point.

We needed to add a valid graphics API for the web. There are only two choices: WebGL or WebGPU.

WebGL is "old" technology that's massively used across the web. It's based on OpenGL ES 3.0. For those who know OpenGL, it started as a fixed-pipeline rendering architecture that evolved over time to offer a more dynamic approach as the needs of developers changed and evolved to push the hardware in the quest to meet players' and customers' demands for higher graphic fidelity. OpenGL ES is the version used for mobile and embedded, which is heavily restricted compared to regular OpenGL.

These old APIs were "replaced" by new APIs that arrived around 2015 with the arrival of Metal, followed by DirectX 12 and then Vulkan. These new APIs offered more freedom to developers and are now the de facto standard for most graphics applications. As the web continued to progress, the demand to access these APIs, which are way more powerful, grew stronger and stronger.

Eventually, W3C decided it was time to offer an alternative to WebGL, which is WebGPU. It gives a similar interface to these new APIs, as it's built on top of these platform-specific APIs. This means that WebGPU is basically choosing, per platform, which graphics API it will call below (DX12, Vulkan, OpenGL, Metal…).

The advantage of WebGPU is that it's been designed to work natively or on the web. By natively, I mean it can run in any desktop application while being cross-platform. The disadvantage is that support for WebGPU is relatively low compared to WebGL. WebGPU was officially released on all browsers early this year. It came with some implementation bugs, untested paths, and some unsupported features. For example, Firefox on Android doesn't support WebGPU yet, while Firefox on desktop does.

The promise of WebGPU is simple. You write the code once and it works everywhere.

I decided that we would use WebGPU and not WebGL. The reasoning was simple:

  • SGL, our graphics library, uses Metal, Vulkan, and DirectX12. These new APIs are somewhat incompatible with the way OpenGL, and by extension WebGL, works. I didn't want to introduce hacks to ship our game to the web. WebGPU matches these APIs mostly since it's built on top of them.
  • WebGPU allows us to validate on desktop that everything works before going to the web. This way, we know the result should be working on the web if it works on native.
  • The support was broad enough in 2026 to actually make the move and do the porting on WebGPU.
The Plan

With all this information, here was the plan to port the game to the web:

  1. Upgrade the core libraries: either because it helps or because it's required.
  2. Implement a rendering backend for the web (WebGPU).
  3. Use Emscripten to compile our C++ code to WASM for the web.
  4. Validate that everything works and enjoy the win.

With three weeks in mind, I knew that was a lot to do, but it was feasible. The objective was to take two weeks for parts 1 and 2, and the last week for parts 3 and 4. As you'll see, many complications, issues, and surprises arrived in an order different than expected.

In the next blog post, I'll discuss how I tackled Emscripten, how it works, what it does, and how it impacted our porting process.

RSS Feed
Previous Post Blog List