Sparrow WebGL Devlog 1: My New WebGL Engine

Creating a WebGL engine with vanilla JavaScript

12.04.2023 - 12:02
I love game development, but I haven't been able to work on as many games as I used to. For several reasons, I got stuck in the development of my indie game and haven't made any progress in the last two years. I still want to finish the game, but I would need a few months without a lot of distraction to refamiliarize myself with the project, do some necessary updates to the engine, and add a lot of content. Sadly, I need money, so it's unlikely that I'll have enough time to finish the game any time soon.

However, I still want to do some game development even when I'm not working on a bigger indie game. I have some ideas for smaller casual games that wouldn't require as much time.

Another sad reality is that when you want to develop a game, the best option is to use an existing game engine like Unity or Unreal. It's much easier to support multiple platforms and give your game the best chances for success. For me, however, creating the engine is at least half of the fun of game development, so I don't like to use existing frameworks. For my indie game project, I am also using a self-made C++/OpenGL engine, which works quite well, but it would need a few major updates to modernize some features, which would take a lot of time.

Instead, I am choosing a compromise: WebGL. Because WebGL runs in the browser, it's already platform-independent. Some WebGL and JavaScript features may not be supported by some niche or outdated browsers, but the core WebGL API is pretty widely adopted by now. Even the newer WebGL 2 feature set is supported by all major browsers, so that's what I'm going to use. Additionally, JavaScript comes with a few advantages over C++ too. For example, C++ on Windows does not support Unicode in the default std::string class (it does on Linux) and when I started to work on my C++ engine many years ago, I didn't care about non-ASCII characters and just used std::string everywhere. Only when I thought about publishing a game, Unicode became an issue because not everybody wants to or can play everything in English, so it might be a good idea to localize the text in the game, which isn't possible without non-ASCII characters. Making all of my C++ code Unicode compatible would be a massive task. JavaScript, on the other hand, automatically supports Unicode, so I get that feature for free.

C++ and OpenGL are faster than WebGL, but the types of smaller casual games that I want to work on don't require top-of-the-line performance anyway. You might comment that there are some existing WebGL engines like Babylon.js or Three.js, that I could use. I like Babylon.js and I have used it for multiple freelancing projects that required WebGL. However, I don't want to use it for game development. I have used it for a Ludum Dare game, and that game had some unexpected performance issues. I'm sure, it's possible to fix them when spending a bit more than the 48 hours I had during Ludum Dare and Babylon.js would be perfectly fine for game development. The main reason, why I don't want to use it, is that as mentioned above creating the engine is a lot of fun and I know that would enjoy working on the games a lot more when using my own engine. Additionally, even though I'm using WebGL for 10 years, I have never created a proper WebGL engine. And finally, it's always nice to minimize the number of third-party dependencies as much as possible.

With JavaScript and WebGL greatly simplifying some annoying requirements like platform independence, Unicode, and general JavaScript convenience over C++, I can work on the more interesting engine features without wasting too much time on grunt work. Combined with the smaller scope of the games I want to work on, I can hopefully publish one of them at some point.

I want to write about the development process of the engine and some of its features, as well as the development of the games when I get to that stage. While I already did a lot of groundwork, this first blog post is an introduction to the project and I want to talk about my goals and motivation. Future stories are probably going to focus more on technical details.

Sparrow - A Misleading Name?

Before starting the WebGL engine, I wanted to give it a name. In the past, too many of my projects had very generic names with the worst offender being my indie game itself, which to this day is still called Project 2019 because that's when I started it (trying to come up with a good name for the game is one of the reasons why I got stuck). As mentioned, I want to blog about the development process of the engine, which is another reason why I wanted to have a name before I started. And finally, the engine needs some very generic class names like Quad, Texture, or Button, so I want to use the name as a namespace, which again, requires one before the first line of code is written.

When trying to come up with a name, I looked out of my window to the bird feeder, which has lately been occupied by a swarm of sparrows (who tend to bully all other birds that try to come close). It’s a common English word, so there are a lot of programming projects already called Sparrow, but at least there didn’t seem to be a very popular one. However, this was before I learned that Deepmind’s new large language model is called Sparrow. When I heard about it, I had already put a lot of work into the engine and I didn’t want to change it anymore. I quite like the name Sparrow and got attached to it.

Unfortunately, some of my stories about the development might be misleading for some people if they expect them to be about the Sparrow LLM and not my - in the grand scheme of the internet - irrelevant WebGL engine.

Design Goals

Even though I started my C++/OpenGL engine many years ago and have learned a lot since then, I like its general layout and usage patterns. Therefore, I'm going to model the WebGL engine after it. I'm going to change some things that I don't like, but a lot of features are going to be the same or very similar. Not only am I used to working with it, but it will also make it easier to switch between the two when I have to.

One of the things, I need to figure out is which license to use for the engine. In general, I want it to be open source, especially for personal and non-commercial use, but I'm also quite poor, so if there was a way to get some revenue from it, that would be great too. I'll have to look into this, but legal language is very confusing.

The Basics

Before diving into the more interesting and fun aspects of developing a game engine, I had to get some of the basics out of the way. This meant setting up the main classes and handling the HTML canvas element. One of the main issues of my C++/OpenGL engine is that it doesn't really support high-resolution screens, because when I started to work on it, high-resolution screens weren't as common as today, so I never bothered to make my engine compatible with it. Nowadays, however, high-resolution screens are very common, and unlike before I have one of my own so that I can test high-resolution scaling myself rather than having to ask a friend to awkwardly test something 20 times.

Additionally, high-resolution screens are especially common on modern smartphones and laptops and unlike my C++/OpenGL engine, which I never planned to port to mobile devices, a WebGL game or application is very likely to run on a much wider range of platforms. A while ago I learned how to properly scale canvas elements, so I added this feature to my WebGL framework from the beginning. However, scaling WebGL properly for high-resolution screens is a bit more complicated than HTML 5 JavaScript drawing, and I am probably going to talk about it a bit more in upcoming blog posts.

The first rendered components I added were quads and texts. One of the very convenient features of JavaScript/WebGL that saves a lot of time is how easy it is to render text. You can just use the OffscreenCanvas class (or a normal Canvas element) to draw some text and then convert it to a WebGL texture. It's not necessarily the fastest way to render text, but unless the text changes very frequently, it's fine. Realistically, a lot of text is created once and then never changed again. I used the text to add some simple debug information to the top because it's always nice to see frame times and frames per second. I might write a tutorial about how to do this text rendering trick because it's quite a neat method.

Finally, I added some 3D shapes (cubes and spheres) because they look better on a screenshot than just a few colored quads and maybe get a few more people to click on the story. However, I am probably going to focus mostly on 2D features in the beginning, because the game I want to work on is 2D and also, in a few weeks, the next Ludum Dare game jam is scheduled and hopefully, I can implement the most basic features by then to use my WebGL engine for the event. Although, I need to find a good solution to combine all individual files into a single minimized JavaScript library before that. Most existing tools require a lot of complicated-looking configuration and basically, all I want to do for now is copy everything into a single file and remove all unnecessary whitespaces and comments, so I might just create a simple script to do this.

Colorful 3D shapes with some debug info at the top:

Objectively, creating your own engine when you want to develop a game isn't a good idea. It's easier and faster to use existing solutions like Unity or Babylon.js. However, I like creating engines, so I'm going to do it anyway. The chances of creating a commercially successful game are quite low either way, so I might as well have some fun while working on the game. The early stage of development is particularly enjoyable when you don't have to worry about breaking twenty existing systems when you want to change anything. I'm looking forward to working on it a lot more and hopefully, it doesn't join my ever-growing pile of unfinished projects any time soon.

by Christian - 12.04.2023 - 12:02


Social Media:  © 2020 All rights reservedCookiesImpressum generated in 8 ms