An advice for designing games
As I’ve grown as a game developer over the last year, I’ve published three games that you can play right now in your browser:
- Nov ’21: vast, a puzzle-platforming-programming game that mixes coding and 2D platforming gameplay.
- May ’22: Download Speed, a racing rogue-lite in which you play a file being downloaded.
- June ’22: Wake Up, Felix!, a reverse bullet-hell (think Vampire Survivors) starring Felix the Cat on top of a clock.
All of these games were made with the following priorities: learning and practicing game development, enhancing my portfolio, having fun/basking in creativity, and (maybe) winning some money.
First, I believe in the potential and upsides of web gaming. For example, many games made for the jams I’ve participated in require a user to download a
.exe or .app file to run. This executable is typically not code-signed (i.e. you have to say “Yes, Windows/Mac/Linux, I am fine running this totally unknown and untrusted binary!”). That’s understandable for a non-commercial game product.
The low barrier of entry for players to engage with your game will always be browser gaming’s strongest feature.
As the years go by and browsers improve their feature set and capabilities (e.g. WebGL 2.0, gamepad support, and other APIs), the idea of what a “typical web game” looks like is being challenged and I find that exciting.
In the above Felix the Cat
.gif, the timer, health bar, and damage numbers are actually React components on a layer on top of the game
<canvas>. I’ve employed this tactic with a front-end framework or just the DOM API to mix in traditional web elements that feel like they are a part of my game, but actually exist outside of it (or, on top of it).
Game development is essentially UI development, and I’ve carried over many things I’ve learned to my traditional business-oriented work.
3D visuals on the web are also becoming a hotter commodity for branding, marketing, and many other products and experiences. Working on my games teaches me things that are helping me build experiences that people will pay for!
My first web game:
I had made a few toy games before vast, but I decided to participate in Kajam 2021 game jam in order to build and publish my first fully playable game … however it turned out. I only had one week to do it!
Choosing What To Build Your Web Game With
My choice for this game was easy (because it was selected for me), and I enjoyed working with Kaboom. But there are lots of options I could have selected (e.g. Phaser, Babylon, PlayCanvas, PixiJS, and more). Tool selection paralysis is a real productivity killer for otherwise prolific and creative coders. I feel for anyone stuck here; it is a bit of a trap. Here is how I think about, categorize, and strategize around this stuff:
- Understand that you are choosing a tool that will ultimately render visuals to a
<canvas>element in HTML. I would highly recommend learning about the canvas element on its own and build some simple, controlled visualizations with it. When you’re making a game, you are essentially receiving player input and mixing it with visual content. The
<canvas>element will always power the visualization of that content, unless you’re building your game with DOM elements only. Learn what it does!
- Notice the difference between a game “library” like Phaser, Kaboom, Babylon; a game “engine” like PlayCanvas; and a “rendering library” like Three.js. With a “game library”, your development will be non-visual/code-centric, but you will be equipped with lots of utilities and organizational tools that benefit most games like sprite animation and input modules. With an “engine”, you have a GUI where you can visually transform the objects in your game like you were using Photoshop. You’ll write code to attach to objects and sometimes other contexts. This is a popular way of building games evidenced by tools like Unity and Unreal Engine, due partially to their scalability and accessibility when you have a large team with disparate skill sets. With a “rendering library” like Three.js, you have abstractions to visualize content on your canvas and not much else, and very little that is oriented towards building games specifically. Strangely enough, I’ve built most of my games without a game library or engine, instead just this “rendering library”. I delve into the reasons why later.
- Know the visual capabilities/biases of your tool. Although they will all render to
<canvas>, most libraries are built with either 2D graphics (e.g. Phaser, Kaboom) or 3D graphics (e.g. Babylon, PlayCanvas) in mind. Pick one that matches your vision!
- Recognize the needs of your scale. Clearly a lot of the advice and perspective in this article is from an individual developer making small games, not a large team creating a massive experience. Think about how collaborators will work with you on your product, especially artists and other coders. It’s always best to tailor your technical choices with scale and productivity in mind.
You can produce a great game in any tool I’ve mentioned so far. So, just pick one and MAKE SOMETHING! Learning one will carry over to another you may try in the future.
Kaboom was nice to work with and didn’t feel feature bloated, which I think is something that makes Phaser hard to use. Here is some example code from vast that shows a lot of different stuff at once:
That being said, I had to work around lacking control of certain mechanisms like controlling how gravity and platforms work. I talk about a lot of my challenges and process in this video devlog. There is one more thing I wanted to highlight before moving on: vast’s in-game programming interface.
The primary way of playing vast is to write code that affects your environment. The execution of this arbitrary code that a player will write is achieved with
eval which I would argue is an extremely rare use case for it. More importantly, the visualizing and editing of this code is not from Kaboom on the
<canvas>, but instead a DOM element layered on top of the
<canvas>. This also goes for the dialogue boxes that start appearing in level 7:
I emphasize the usage of DOM elements (and by extension any front-end frameworks like React, Vue, Angular, etc.) in my games because they suggest powerful control and visual freedom in your game UIs, as well the usage of interesting elements like <video> (e.g. webcam stream). And you might already know this stuff! If you’re a web developer reading this, I hope recognizing this seems empowering and full of possibilities!
My work on vast brought confidence that I can make a game I am proud of with tools and platforms I already know well. I consider vast to be my most personal game so far— the idea for it springing out of my day-to-day programming andragogy, and the horror themes inspired by my own nightmares. It has a limited player base (requiring programming knowledge to play) but I’ve received great feedback on it from those who have played. Thank you if you are one of them!
If you need hints on completing vast, feel free to contact me! The game isn’t over until you’ve beaten the final boss.
I was excited to do another game jam and build something else … eventually. Half a year later, I found the time and made my next game:
Download Speed is (I guess) a racing game, stylized around the idea that you are a file making its way from origin server to user computer — with only 2 minutes to do it! It renders through Three.js, and the entire UI and HUD is built with DOM elements, including our friend Blippy!:
It was made for a casual game jam with the theme “Time is Money” and a required special object “Clip”. One of my favorite things about doing jams is working under the constraints of their themes. In Download Speed’s case, “Time is Money” prevails in the game’s upgrade system/economy in which you have to spend remaining time to get faster. “Clip” hopefully is obvious, and including a character like Blippy really drove the file download lore in the first place!
As I said, I made the game with Three.js, which is not at all a “game library”, but more of a “rendering library”. Let’s explore that:
Three.js Basics in Brief
Three.js is a library that abstracts built-in browser WebGL methods to allow you to conveniently render a 3D scene to a
<canvas>. A 3D scene is made up of the world space, the objects within (which include shapes, models, lights, etc.), and at least one camera viewing the space/scene. You have access to a mechanism that renders an image (usually called a “frame”) of that space and transformations of the objects or the camera in that space will be reflected in subsequent images. The Three.js library also comes with utilities to calculate things within the world space like linear algebra functions and constructs, but not much in the realm of “game making” utilities.
Three.js is not a library for making games specifically. A lot of people run into this truth early when looking for basic game functionality like listening for user input, detecting collisions, or controlling the camera in a typical way. I built a first-person character module to use in Three.js, because it has none:
Download Speed never ended up having this freedom of movement, but did evolve from my work in implementing a first-person perspective character controller in Three.js — seen above. This character can move, look around, jump and fall, walk up slopes and slide down steep slopes, and interact with objects at a configurable distance (like shooting a gun).
I implemented this controller from scratch with mathematical concepts that I have been studying for about a year. I used a gutted version of it for Download Speed, and am using it to drive the gameplay in a larger, work-in-progress game.
Although this takes extra effort beyond using a built-in controller in a game library or engine, working at a low-level with Three.js gives me control and flexibility to create systems that I can adapt at a fundamental level, which leads to more unique gameplay experiences.
“So, do I have to do math to make games?”
You don’t, and you can absolutely build great games without learning a ton of math. However, you do achieve a level of expressive freedom and easier debugging if you establish a strong foundation in linear algebra. Here is what comes up as useful for me very often and I recommend that you eventually study, too, if you’re serious about game development:
- Understanding vectors and how to operate on them. This includes vector addition, subtraction, multiplying by a scalar, and dot product. Thinking in vectors helps in both 2D and 3D spaces to get your objects interacting with each other and moving the way you want them to. Vectors are how you describe position and orientation in a world space, like calculating the direction and distance between your player and an enemy, or if they are looking at each other. Learn them well, from multiple sources!
- The nature of the sine and cosine functions. I have to shout out this article for helping me understand how these functions can be used to generate a cycling value which I have used a ton in my work since. Simply combining a timestamp (e.g. from
Date.now) with a
Math.sin/cosis enough to do wildly awesome stuff.
- Nested world spaces and applying matrices. Do not sweat all of the matrix operations details and instead work on conceptualizing how coordinate spaces exist in a hierarchy, and how matrices can be used to apply transformations from one coordinate space to its parent, and vice versa. This is very related to using
Groupin Three.js and can save a lot of headaches when debugging those.
This chapter of “3D Math Primer for Graphics and Game Development” really helped me with this concept; the book is a solid though challenging study in general. I learned about the maths I need while learning other things but I’d estimate I’ve been focused on it for six months so far.
On Learning Math
I understand the hesitancy to learn math; I honestly hated math in school. You’ll be glad to hear that math feels applicable in virtual spaces like making 3D scenes, immediately answering the classic question of “What would I use this for?”. My process for learning what I needed to know was to encounter the problem, or desire for a specific effect, then learn the math to solve that problem or achieve that effect.
I want to reiterate advice to learn math from multiple sources. Math can really feel like alien magics until the correct person has explained it to you for the 5th time, possibly in the best way your brain thinks.
I think that the strongest line through math and coding I have found is the “conceptualize-to-apply” nature of their constructs.
Math.cos is akin to using a
for loop, in the way that you need to first strongly conceptualize what they do independently, discover practical usages for them, and then practice them while solving different problems.
Learning this type of stuff is optimized by different perspectives, because we all think differently from one another, and communicate ideas in very nuanced ways. Here are some resources that helped me a lot, for your variety:
Code + Gifs For Your Perusal
Here are screenshots of code with in-game footage:
A speed fruit is a round-shaped mesh, given a material color and radius (size). It’s added to a group that will be appended to the scene elsewhere. Source code.
Usage of post-processing shader passes with the Three.js renderer to enhance the feel of the game. Source.
Giving your Game “Juice”
If two games play exactly the same (think about how many 2D platformers are out there), but one feels FUN and the other feels dull, it’s probably got a lot to do with “juicing”. Juicing is a term that game developers use to describe the usage of sound, visuals, vibration, and other stuff to enhance the “punchiness” of a game — the often unrecognized but subconsciously felt polish that players need and expect from games that are satisfying to play.
If you’re playing an action-packed first-person shooter like Doom or Call of Duty, and you are struck by an enemy (or their weapon) … what do you expect should happen? I encourage you to take a second and envision you are playing this game, or building it, and think about all the things that might happen because of this moment of your character being struck.
Some things I thought of: a flash of red screen, a hit sound (maybe a robust “argh!”), a blurry screen (especially if struck by an explosive), a brightening/darkening of a health meter to describe lost health, a rattling of the camera or controller vibration. What else have you seen before?
Now imagine playing a game like this, getting hit by an enemy bullet or fireball, and having … none … of these happen. Not only does that game feel less fun, it is also more confusing and frustrating to play.
Did I just get hit? By what?!
It was really important for me that Download Speed felt fun to play, being such a short and hopefully intense experience. I wanted the player to feel the difference between slow and fast. I wanted players to grab speed fruit not only to become faster, but because it was satisfying to get them. I wanted an experience that builds in intensity from beginning to end.
Here are a few ways I tried to “juice” Download Speed, with some implementation details:
When a player grabs a speed fruit
- The screen flashes green or pink depending on the color of the fruit. This is done with a post-processing renderer shader program with a color uniform.
- A strong boost of speed glides them forward. This is a degrading scalar applied to the user’s base speed vector. This is also simply a base mechanic of the game (Blippy calls it a “boost”) to encourage players to chain fruit pick-ups together.
- A pick-up sound plays, lower-pitched and quieter for normal fruits, high-pitched and louder for super berries (the pink orbs).
- A blur level based on the user’s current speed. This effect is way more obvious at high speeds, and is implemented with a GLSL post-processing shader on the renderer.
Shader programs are available in most renderers (e.g. like the ones used by Unity and Blender) and they are a way of informing the GPU how a material or image should render.
I found learning how these shaders work to be so far indispensable in producing interesting visuals in my games, and solving some really hard problems (like the texture issue in Wake Up, Felix!, later in this article).
This is a pretty involved concept and this article is long, so I will simply demonstrate and share some stuff and move on:
- “Learn GLSL Shaders from Scratch” by Nicholas Lever on Udemy. This course was extremely informative but difficult. I recommend it highly if you love to experiment and be challenged in your learning.
- The Book of Shaders is an unfinished classic and something I reference occasionally.
- My Shader CodePens, which includes demos from the above resources that I recreated or forked, and my own experiments. I go back to these to implement visuals in my games often.
And here is how I used shaders in to enhance visuals in Download Speed:
Sometimes jams have huge cash prizes ($25,000!), so right after I finished Download Speed, I worked on:
Wake Up, Felix! was made for Together Jam 2022, sponsored by NBC Universal. I was allowed to use Felix the Cat, and the theme was “Power in Numbers”. I’m proud of how I matched the theme for this game: you are offered a new weapon every minute, for the matching number on the clock.
This game, like Download Speed, was made with Three.js which may immediately seem weird since the game feels mostly 2D. The 2D sprites are actually textured materials rendered to Plane geometries, and are moved around in a 3D scene. You can sense that the camera rotates to show this mix of 3D and 2D, and there are usages of lighting on the clock and weapons to accentuate the mixed perspective.
Here is the camera rotation massively exaggerated on a local copy of the game, to show an extreme version of this visual effect:
I was inspired to make a game like Vampire Survivors because I thought it would be an appropriate experience to have scores and leaderboard, which was another requirement of the jam. The leaderboard also includes which weapons you chose!
One of the most important things for me with this game is that I could put TONS of enemies on the clock, especially near the end of a run. I ran into performance issues early on that slowed the game down when I tried to spawn even 10–15 enemies at once.
Three.js’s Texture First-Time Render
As mentioned earlier, the 2D sprites in this game are in fact 3D meshes with plane geometries and a material rendered on one side. At first, this material was a MeshBasicMaterial with a texture map.
When a texture is first used for a material, there is a “first-time render” step that Three.js takes to calculate and cache information about how to render the texture. This applies to every texture in your scene, even if it is the same kind of texture you’ve already used (e.g. the mammoth sprite). What that meant for me is that every enemy would stutter the game on spawn, and it played awfully.
My first attempt to solve this issue is to share the same texture for every enemy across all spawns of the same enemy. This solved performance issues but didn’t work because if I flipped the texture (to turn the enemy around), or changed its offset (to animate the sprite), it would apply that transformation to EVERY enemy of that type on the screen. Pretty awkward.
I finally solved the problem … with a shader!
This shader receives the same loaded texture for the same enemy, but allows for independent control on a single mesh. This shader plays the animation, flips the texture when needed, and even handles the “hit flash” when enemies get hit.
I am proud of this code, as I feel my ability to write it came as a culmination of learning and practice I had been grinding on for months. And, it solved a major problem in my game!
Now I can have TONS of enemies, see?:
All the 2D art in the game was drawn by my good friend Hunter, and all the music/sound by my partner Elissa!
I also used this jam to stretch my own artistic skills by making all the 3D models in Blender!
I appreciate you reading this article and playing my games. Making these products takes tons of effort in order to learn new skills, build in efficient ways, and fulfill a creative vision. Every play of any of my games is cherished. ❤
I hope that this article introduced you to interesting concepts, tools, and thoughts.
Some Parting Advice for Aspiring Web Game Developers
- Start with 3D fundamentals first. Using a program like Blender is a great way to start. Build your own understanding of 3D world space, geometries (vertices/edges/faces), position, scale, rotation, materials, etc.. Every concept you may encounter in Unity, Unreal Engine, Three.js, or another 3D platform will likely be found in Blender, where you can toy around with it, and press “Undo” a bunch.
- Participate in a game jam. Game jams are highly motivating for me and a great way to learn from others. It is also useful to work at a small scope especially if you want to practice building and actually finish projects. If you want to collaborate on a jam with me in the future, please reach out to me! I am open to working with others, even if you have limited experience.
- Get out of tool selection paralysis and just make. This is important advice for any programmers making anything, but tooling doesn’t usually matter as much as your concerted efforts. The best things to look for are documentation, community, and available examples.
- Harness the web! I have other, long-term games in the pipeline that use Chrome Extensions as part of their gameplay, and WebRTC video streaming! As I mentioned earlier in the article, I think the browser’s potential for unique gaming experiences is massively unexplored. Others may convince you, a web developer, that you have to switch your platform to make a compelling game. YOU DON’T!
Thanks for reading!