And then is it really building something if you are porting it?
tl;dr
Two games I wrote over 10 years ago for iOS, Android, Blackberry, HP Touchpad, Windows and Mac now run in a web browser. Norkal’s Space Maze of Doom runs here, and pX is available here. You can also look at www.phunsta.com if you are really bored.
*Sorry if this blog veers a little into the geeky/tech. I found the technology I was playing with was astounding. The fact that I was able to do what I did blows my mind.
The story begins September 2011 when I started to develop a game for mobile devices. I released my first “proper” game July 2012 almost a year later. Norkal’s Space Maze of Doom NSMOD) was a true labour of love, I poured my heart and soul into it. I thought about and cared about every little detail. You may not agree with my choices (tbh I am not a designer) but I agonized over them and was very happy with how NSMOD came out. Unfortunately, the rest of the world did not agree, and the take up was underwhelming. I did not quit my day job.
pX was the second game. Whereas NSMOD was a labour of love where I agonized over every detail, pX was not. What was I thinking with those audio choices!! pX was an experiment, did the masses want a simple “arcade” game with no story but lots of replay value? They didn’t (well not from me). I am proud of how pX turned out, it is rough around the edges but is incredibly addictive.
One of the geeky things I was particularly proud of was that to implement both games I built a truly cross functional game engine. NSMOD and pX both ran on iOS, Android, Blackberry Playbook, HP Touchpad, Windows, and MAC. They worked at any resolution or orientation, and now they are lost to time. So being a geek I wondered how hard it would be to port it to WebAssembly, turns out not that hard (with caveats).
The game engine (and games) was written using C++ and OpenGL. For each platform I then had a light native shim which created a viewport and handled input. This approach allowed me to easily support the different platforms. I was able to get it running on Android and WebOS within a week each.
According to ChatGPT: WebAssembly, often abbreviated as WASM, is an open standard that defines a binary-code format and a corresponding textual assembly language for executable programs. It’s designed to be a portable compilation target for high-level languages like C, C++, and Rust, enabling deployment on the web for client and server applications.
This meant that I could take my original C++ code, compile it and have it run on most modern-day web browsers, and I must say that was pretty much the case except for ironically phone and tablet browsers but more on that later. I started with pX since it was the simpler game and I was able to get it up and running in two weeks.
There were some challenges. The main one was that I was moving from synchronous file (asset) access to asynchronous. I went from loading assets such as images, fonts, audio files from the devices local file system to loading them over the web via web calls. I could have embedded them in the WASM file but that seemed wrong.
I asked ChatGPT to write some codes to fetch file data from the web and access it in my c++ code. ChatGPT quickly returned 10 lines of code which looked like it should work, I implemented it and sure enough it did not work. I told ChatGPT that it did not work, it agreed and made some additional changes which still did not work. We went back and forth on this for a while. I then used Google to debug individual lines with no joy. Eventually I Googled from first principles and found a one-line emscripten (WASM compiler) command which did all I needed; problem solved.
Sidebar: I have mixed results with ChatGPT (or Codium). I find it useful for easy things like makefile syntax (don’t judge me) or simple c++ syntax (I am a bit rusty), but so is Google. For more rare use cases (like anything to do with WASM) it is useless, but is useless in a confident way that takes you down a garden path.
After getting the web fetch code working, restructuring pX to work asynchronously was a dawdle, NSMOD not so much but more on that later.
My next challenge was getting the audio to work. I saw that WASM supported OpenAL (open source audio library) so I was very optimistic that this would work out of the box (I mean MyPNG just worked). Alas, I could not get it to work, probably something I did and might have been related to the async change (who knows), or maybe just this version was different than what I had used and I was too lazy to fully understand what I was doing.
There was also an added twist that most modern browsers won’t play any audio until the user initiates an action. This is to prevent spam ads playing sounds without the user’s permissions.
Sidebar: I have mixed feelings about this, should Chrome be controlling how websites want to present their content? If random audio was annoying wouldn’t people just not visit that website? I guess I am naive.
In the end it was not a hard problem to solve since to play the game you need to click a mouse (a user-initiated action), but it did trip me up for a bit until I noticed the console log “clue”.
In the end I came up with a solution which leveraged the HTML/JavaScript audio support. I created an audio node on the DOM and then played it from the c++ code. WASM allows you to create and call JavaScript straight from the c++ code which is kind of cool.
pX was now running, after about two weeks, boy was I chuffed. I figured NSMOD would be a dawdle, after all I had done all of the hard work. Turned out to be more challenging than expected, mostly due to issues of my own making. The first issue had to do with how I created pX. When I created pX I just forked the NSMOD code and made the appropriate changes. I did not bother supporting any shared libraries or code. I just wanted to get the experiment out as quickly as possible.
For NSMOD I thought it would be easier to use the same game engine and share as much code as possible with pX. This was relatively easy to compile but there were some very subtle differences in the way I handled fonts, dialogs, and various 3d planes between the two code bases. This was all very painful to debug, but I was impressed with the fact that I could use the Chrome debugger to step through c++ code and set breakpoints.
The next issue was around the fact that NSMOD had a more complicated memory/asset management model than pX. NSMOD had a lot of assets which it would load and then destroy depending on the level you were on. The async model I used for pX was causing me all kinds of grief, I had issues getting it working cleanly, and performance was also an issue when moving to a new level. I decided to totally change my strategy. I now copy the assets into the web’s local file storage and then access then with good old fashioned c++ synchronous fopen/fread/fclose commands. A far simpler solution which meant I did not have to change my startup code much for NSMOD.
The final issue I had was getting the mouse wheel to work. There are a couple of tile dialogs in NSMOD to choose the next level, or browse achievements. Since NSMOD was designed for touch devices it basically used the touch and scroll paradigm, I just wanted to added a scroll wheel shortcut. This should have been pretty easy, after all I had the mouse move/up/down pattern working. Nope. I tried all kinds of approaches, then hacks, but no luck. I eventually got it to work by changing a parameter from a true to a false. Still not sure what that parameter was supposed to do.
The last piece I wanted to do was to add a leaderboard to pX. I just think it is fun to see how others (all 3 of you) do, and add a sense of community and competition. I looked at a couple of public leaderboard APIs, but they all expected users to create accounts which did not feel right to me. AWS looked like it had a good system but I could not create an AWS account (see my other post). In the end I signed up for a Google Cloud account and wrote a simple leaderboard service. We have until April 2024 before my trial runs out to run up that leaderboard.
I still have one more problem to solve; pX and NSMOD do not run on iPhone or iPads (I have not tested on Android). How very disappointing, the console logs says that the browsers do not support certain OpenGL commands I am using. I think I will look at it further but not today.
I am glad this is all done, it has been a lot of fun, I learned a lot and got a chance to code something that can change the world. I would have been done sooner if it was not for some annoying distractions like a kitchen reno, trip to Vegas, and Baldur’s Gate 3.
I hope you enjoy