1. Introduction to GameSnacks and Game Optimization
Welcome to making bite-sized games with GameSnacks. The web has the advantage of easy accessibility for gaming, but large file sizes hinder this. Gamesnacks is working on optimizing game sizes. Using gzip encoding and showing visual elements quickly improve user experience.
Hello, and welcome, everyone, to making bite-sized games with GameSnacks. I'm Alex Hawker, I'm a software engineer on the GameSnacks team at Google. My main focus has been a variety of web gaming infrastructure and distribution-related projects, many of which revolve around this very challenge of optimizing web game file size.
Now, you may be thinking, why bite-sized games? Aren't internet speeds good enough these days? Well, one of the key advantages of the web as a platform for gaming is its capability for virality, perfectly expressed in examples from io Games to the recent hit, Wordle. But this advantage of easy accessibility is severely negated when file sizes soar too high. Despite advances like 5G, much of the world still frequently experiences slower connections, from Australia, to India, to right here in the US. I know I've certainly experienced many times in places where my phone's internet speed suddenly slows to a crawl. And in times like these, when even just a 10 megabyte game can take over 3 minutes to download, every byte counts. No one wants to see that blank screen on the left, they want to play some games.
Well this is one of the things that we're working on here at Gamesnacks. Gamesnacks is a new team at Google dedicated to growing the web gaming ecosystem to help make games universally accessible and useful. As part of this effort, we've experimented heavily with optimizing games and their file sizes. Even going so far as to build an experimental game engine to see just how far you can take these optimizations, and how worthwhile the various types can be. I'm here today to share with you the highlights of these learnings. We'll start with a few general considerations that apply to optimizing game size then dive into specific optimizations for different file types.
First off, using gzip encoding on game files is an easy first step to improving file savings. On average, it reduces the file size of code and other applicable files by 75%. For those unfamiliar, gzip is essentially an open source equivalent to zip. Browsers today support servers sending them these gzip-compressed files, which allows us to reduce how many bytes we need to send. However, due to the way gzip works, it doesn't work well on every file. You should instead only use it on uncompressed files like code, configs, and specific formats like svg or midi. Unlike other tips, using gzip is not a change made to the game project itself, but instead has to be enabled on the server. Fortunately, this is usually pretty straightforward. For instance, in Google Cloud Storage, you can just add this Zed flag with the list of extensions you want gzipped.
Next up, we've got well this is kind of an awkward pause. Not really all that pleasant, which is why it's so important to get some sort of visual shown to users quickly. This is critical, because research shows that just having the page load time go from 1 second to 5 seconds increases the bounce rate of the page by 90%. So showing the user some ideally animated visual — whether a splash screen or a loading screen — reassures them that things aren't broken, and gives them something to focus on to make the load feel faster to the user. So even though this won't improve the total load times, or likely might even make it slightly worse, it's overall a very big win, since at the end of the day, the user's perception is what matters, not the raw metrics. In a similar vein, lazy loading content can be a big win.
2. Lazy Loading and JavaScript Size
Lazy loading is a technique where you only load the content that you need at the current point in time. This can be done on a fine-grained level, loading assets on the fly and using placeholders. It helps get the user into the game faster and reduces the average download required. JavaScript is relatively small compared to Visuals and Audio in terms of file size, but it can be heavier than it seems.
This is when, rather than loading everything eagerly ahead of time, you only load the content that you need at the current point in time. So if we had this space game with four worlds, maybe we'd only load the core game mechanics and the rocket ship level assets first, and defer the rest to be loaded later when the player gets to it. You can also do this on a more fine-grained level, where individual assets are loaded on the fly and rendered with placeholders in the meantime.
This is a popular technique in Unreal Engine with rendering low-resolution versions of textures as a placeholder to speed up loading times. With these techniques, even though the total game size stored on a server doesn't decrease, it really helps get the user into the game a lot faster and often reduces the average download required. There are some trade-offs though, as this does require careful planning and code to handle breaking up the game into self-contained segments, which is easier for some game types and some game engines than it is on others. Additionally, if you're planning on embedding this game on the user's device, for instance with something like Cordova or Electron, lazy loading isn't going to give you many big wins since the user needs the entire thing anyways.
Now that we've covered some general tips, let's take a deeper dive into some key contributors of file size. I've broken down a typical web game from our catalog down into what composes its GZIPed file size, and we can clearly see three major categories emerge — JavaScript, Visuals, and Audio. Let's start with JavaScript. So JavaScript is relatively small compared to Visuals and Audio. Our representative game, thanks in part to GZIP, had JavaScript composing only 20% of its overall size, compared to 43% of Visuals and Audio. However, even though its raw file size is smaller, JavaScript can be a lot heavier than it at first seems.
3. JavaScript Loading and Script Placement
JavaScript is the most expensive resource we send to mobile phones, as it can delay interactivity. Loading JavaScript requires downloading, parsing, and executing the script, which can block the browser and initial rendering. Placing JavaScript correctly and using async script tags can improve performance. Moving regular script tags to the end of the body allows them to download in parallel and run in order. Minification reduces script size by removing unnecessary characters.
As Adi Osmani puts it in The Cost of JavaScript in 2018, Byte for byte, JavaScript is still the most expensive resource we send to mobile phones, because it can delay interactivity in large ways. Indeed, loading JavaScript can have a lot of side effects. Unlike resources like images, loading JavaScript requires not just downloading, but also parsing and executing the script. And if you're not careful, during all this time it can often block the browser from downloading more items in parallel, and even the initial render of the page.
Let's back up a moment, though, and go over how the browser handles HTML. So when you go to a web page, the browser downloads the corresponding HTML file and begins to parse over it. As it goes along it takes each new HTML tag and adds it into the DOM, which is essentially just an in-memory representation of the page. When it sees CSS, it will begin downloading and block rendering until it can fully load it, but it will still keep parsing the HTML. JavaScript is where it gets a bit tricky. Because JavaScript can not just change, but also read from the page's DOM, it technically needs to run immediately or the DOM will be in an unknown state from run to run. As a result, the JavaScript will prevent not just rendering, but also any more parsing until it has fully downloaded, parsed, and executed.
You may notice the asterisk on the slide. There are some edge cases here. Script tags that are marked with async, usually for things like analytics, will not block rendering and parsing. And modern browsers can at least begin loading scripts and resources that are all bunched up together into HTML without other DOM elements in between. So, here's an example of a potential worst case scenario. If your JavaScript is itself loaded dynamically by JavaScript, you can end up in this unfortunate situation where all of your JavaScript loads and executes in series. Which will take a much longer total time than if they could load in parallel like the images in the bottom half. This is all to say that they are definitely gotchas. And if you place your JavaScript incorrectly on your page, it can definitely slow down your load time. But thankfully, the general case solution is actually quite simple. Essentially, you keep style sheets, async script tags like Google Analytics and anything else in the header. And all you do is move all of your regular script tags to the end of the body section. Because all of your scripts are now clustered together at the end of the body, they will all download in parallel. Since there are still normal script tags and not marked async, they will still run in order, which is great for initializing your game code. And since all of this is at the end of the body tag, all of the previous DOM elements will still get a chance to load and render, so you can get something fast to your users.
Now that all of our scripts are in the right place, let's talk about the scripts themselves. One key attribute of reducing script size is by using minification. This is a process where unnecessary characters are removed from your code, such that it will still run the same afterwards. There's a variety of tools to do this.
4. Optimizing Code and Art
Running minification tools with default settings only gets you part of the way. Enabling variable name mingling can reduce code size, but be cautious of mixing different ways of accessing object properties. Be mindful of third-party libraries and consider importing smaller subsections. When it comes to art, realistic graphics have tradeoffs in performance and file size. Stylized games can use simpler textures and materials to optimize for mobile.
From Tercer to Google's Closure Compiler, all of which you'd probably run as part of your build step from something like Webpack. However, just running it with the default settings will only get you part of the way.
One of the key things that is disabled by default in most tools is to also mingle variable names. The exact name differs depending on the tool, but when you turn this on, it will automatically rename variables for you to make them smaller. Quite handy.
However, while this does provide a significant reduction, there is a reason it's disabled by default. It can cause issues with your code. The most common case for this is that you can no longer mix different ways of accessing properties on objects, as they get mangled into different statements. Here, we can see that if you're setting or accessing a property with the dot notation, mangling variable names will give a different result than if you use the bracket notation. Thus, if you're enabling this setting, you need to ensure not to mix these two notations for using the same object property.
If you're minifying only your own code, this is still fairly doable, but it can get really tricky when mixing in third-party libraries. On that note, be careful about going overboard on third-party libraries. While quite helpful, they are often written to handle a wide range of use cases, which may make them larger than necessary. As one example, I remember a .obj loading library that took up 400 kilobytes of minified code. Writing some good-enough code to load in OBJ instead takes around 100 lines. This is not to say that you need to write everything yourself, just that you should audit what you do use and make sure it's the best option you've got. Indeed, sometimes you can even import smaller subsections of your libraries, rather than including the entire thing, which helps mitigate the amount of storage it will require.
Speaking of auditing what you do use, let's talk about art. Specifically, the visual art style of a game. It's a lot easier these days to make realistic games in the browser, which is really awesome. However, it is important to keep your audience in mind. There are tradeoffs when it comes to realistic graphics, especially in terms of performance and file size. These pipelines need much more texture data, larger sizes and many more types of textures, in addition to large data for lighting-related calculations, such as for reflection probes or baked indirect lighting.
In contrast, for a stylized game, you may be able to just get away with a collection textures or even a single small 512 pixel atlas of gradients. Indeed, if you are only using unlit materials, you may even be able to get away with removing normals from your model files. That said, if you are going for a stylized look, make sure you aren't still unnecessarily using the full physically-based rendering materials and pipeline of game engines. Especially if you're targeting mobile, this is more than likely overkill, and will have impacts on performance and file size. For instance, if you want objects to be one solid color or texture, don't fiddle with emission textures on the standard physically-based rendering material. Just use an unlit material. It will likely be faster to render this way, too.
5. Post-processing Effects, Shadows, and File Formats
SSAO is a post-processing effect that darkens crevices in 3D models to simulate shadows. Baking shadows into textures or vertex colors can create a more consistent look. Rendering a shadow object under game objects is an alternative to heavy real-time shadow maps. Choose the right file formats: uncompressed, compressed, and procedural. Lossy compression should not be used on images with data. GIF, JPEG, and PNG are common web formats.
Another example here is a post-processing effect called SSAO, or Screen Space Ambient Occlusion, which darkens the crevices in 3D models to simulate shadows from reduced indirect lighting. This effect can give your game a nice look, but it can be expensive and inconsistent at runtime, especially when attempting to balance for mobile phones.
An alternative option is to instead try baking the shadows into the textures or vertex colors. This does have the potential to increase the file size, which I know is a bit of a different of this talk, but it can create a more consistent look that runs more smoothly on mobile. Like all of these tips, it's about finding the right tradeoffs for your project's goals. Balancing performance, file size, visual quality, consistency, gameplay, and a whole bunch more.
A final suggestion for art style is about shadows. The modern method of rendering shadows is with shadow maps. While these are effective, real-time solutions for shadows, they are also quite performance heavy, as all of your objects have to be rendered multiple times, once from each light for shadows and then once again for the final render. And they may also require some fiddling with resolution, cascades, and other settings to really manage the quality versus performance tradeoff for devices like mobile. An alternative is to instead take a node from classic games and just render a shadow object under relevant game objects. This is much faster, can be customized a bit more, such as how soft you want the shadows to be, and it may also make the gameplay more clear, by allowing the user to see the precise x and y position of mid-air objects, especially helpful for collecting floating coins or aiming complicated jumps.
Besides non-realistic art styles that lend themselves to small file sizes, another helpful action is to ensure you are choosing the right type of file formats for your project. The way I'd like to break this up is into three categories of files. Ones that contain the output data uncompressed, such as BMP and many wave files. Ones that store the output data in a compressed form, such as most common image, video, and audio files like png or mp3. And what I'd like to call procedural formats, ones that, rather than the data itself, store a series of instructions to generate that output data at runtime, like SVG and MIDI. The first category of uncompressed formats isn't used all that much, as it's not very efficient. So, let's instead jump into talking about the second category with discussing compression. We have two types of compression. Lossless, in which the data is preserved such that uncompressing the image gives you a perfect copy. And lossy, in which some data is lost, but to the human eye it looks the same, or at least close enough. One key thing to keep in mind is that you should never perform lossy compression on images that contain data rather than colors. Normal maps, metal roughness maps, signed distance field font images, and the like. Usually these will result in significant artifacts. In fact, here's an example of just that. Doing lossy compression on a signed distance field font file on the left severely changes the look of the text, from clean characters to what looks like smeared wet paint. Yes, it can be a cool effect, but not a surprise you want dropped on you unexpectedly when you just push to production. With an understanding of these two types of compression, let's take a look at some common formats for the web. GIF, JPEG, and PNG are the classic formats, and supported by pretty much everything.
6. Optimizing File Formats and Reducing Game Size
Newer image formats like WebP and AVIF offer better compression and support for alpha. GPU compressed textures can be used while compressed, but compatibility can be challenging. Procedural formats like SVG and MIDI are small and customizable, with performance trade-offs. 9-patch images and tile maps save space and offer creative possibilities. Procedural sound effects and MIDI files allow variation and cohesive themes. Procedural pipelines in 3D tools open up new use cases. Reducing game size enables ease and accessibility. Join Gamesnacks for bite-sized game development.
However, newer formats can be a lot smaller. In particular, WebP is now reaching widespread support, and not only can it combine lossy compression with alpha support, but also compresses better than PNG and JPEG. By switching to using WebP, we saw a 25% reduction in game size. AVIF is a new upcoming file format that seems to promise even better compression than WebP, but it is still getting started on the road to support. Definitely something to keep an eye on, but may not be ready to fully migrate over to just yet.
Another category of image files are GPU compressed textures. With other image types, while they are compressed on the disk, the computer has to expand them to 32 bits per pixel before they can be used for drawing. This is the reason why you may have noticed images take up a lot more memory than you may have expected. GPU compressed textures solve this problem by being able to be used while compressed. However, using them can be very tricky, especially as there are a huge variety of different formats, and mobile and desktop GPUs have little overlap in the types of format they support. That said, if your game engine supports using them, they can be quite advantageous.
We've talked a bit about compression, so let's shift over to the last type of file formats—procedural ones. So procedural formats are what I'm calling methods of storing the procedures to generate the data, rather than the data itself. This can take the form of things like SVG, MIDI, or even JavaScript or shader code. While these formats do have downsides, there is often a big performance cost as a result of having to pre-render an image, perform additional shader instructions, or queue MIDI instruments. They are also incredibly small, and can be customized, which allows you to do some really cool things. In my mind, these are some of the great examples of how sometimes, when you optimize for file size, you also get big wins elsewhere in your project.
So for instance, with SVGs and procedural textures, you can make images that look crisp at any resolution. Number one is 9-patch images, which are used a bit on Android. These are PNG files set up in such a way that you can easily make UI components that can be resized efficiently while maintaining the correct borders and margins and corners. Another one that's already popular in the gaming world is tile maps. Rather than storing a full, massive image of the entire game world, you can instead just store a limited set of modular art. This lets you save a lot of space, but it also lets you do some cool things like having maps from battle damage or letting the player go in with a terrain editor and build their own maps. On the audio side, there's also a few cool use cases, such as procedural sound effects. It's easier to mutate a procedural sound effect so you can make it sound less repetitive when you have a whole bunch of them in a row. There's also uses for longer audio, such as music. With MIDI files, you can play the same music across multiple different levels, but change up the instruments so each level sounds unique, but still has a cohesive theme. And finally, with 3D tools such as Blender adopting procedural pipelines with things like Geometry Nodes, I think there may be a bunch more cool use cases on the horizon.
Just to wrap this all up, the core message I want you all to walk away with is that there are a bunch of ways to reduce the size of your web game, and while there are sometimes trade-offs with downsides or additional benefits, keeping games small really allows you to channel the ease and accessibility that makes web games so great. If you are interested in making bite-sized games, I would definitely encourage you to join us here at Gamesnacks.
Game Engines and Custom Tools
We're currently in an Early Access stage program working with select partners. The top choices for making games on the web are Phaser and Pixie. Unity is also a popular choice. The diversity of engines used by people is impressive. The power to choose what works best for you is one of the cool powers that the web gives you. Building a custom engine allows you to change the way you're building the game and create experimental projects. Your choice of tools often points you in a different direction. Giving the player familiarity can be beneficial, but also be experimental. One question from the audience was should I always try to optimize my game for file size?
We're currently in an Early Access stage program working with select partners, but hope to open it up more publicly in the near future. Here is a link if you're interested in learning more. Thank you so much for your time and have a great rest of your day.
So I think I'll start by looking at the poll results here. So it looks like the top choice that people use when making games on the web is Phaser, which I'm not surprised, or Pixie, which I'm not too surprised, that's also what I tend to use. But it's also interesting that Unity is second because, yeah, Alex, what do you think?
Yeah, I think the biggest thing that stands out to me actually about this, which I think is really cool, is just how diverse the range of engines that people use is. I know, when I was building the talk, I was uncertain if I should like, you know, really focus on one engine or the other. And I think doing it in like a broad general way, I'm really happy I chose to do that, as this shows, you know, because so many people are using so many different things. And I think that kind of power to choose what works best for you, you know, do I want like a complete all-in-one kind of thing like Unity, or do I want to kind of get more into the code with stuff like Phaser or Pixie? I think that's one of the cool kind of powers that the web gives you. Yeah, I like that a lot, and you could even see that there's a non-insignificant portion that just either don't use an engine or like build their own thing, which also kind of adds to I think both the diversity of technologies on the web, but also I think game feel or how people like to I think create things.
Yeah, for sure. I think one of the things that, you know, I've kind of realized like building a custom engine is that it really lets you kind of change the way you're building the game. And I think that lets you create more kind of experimental projects or kind of go off. It's kind of I remember one developer was saying that the tools you use to make something kind of inform what you make. And so when you're building your own custom tools, it really helps you kind of expand and go off the beaten track. Yeah, I absolutely agree with that. And I think I've kind of experienced that myself back when I used to make flash games, like Box 2D was, you know, very common physics engine, that was really the only one that people knew of. And most of the time I could tell that a game was made with Box 2D just because a lot of times if you just kind of leave a lot of the default like friction, a lot of the default momentum, etc. You get that feel. There's nothing wrong with that because a lot of those games are really fun, but it's like you said, like your choice of tools often does kind of, even if not like limit you, but it does point you in a different direction. Right? Yeah, like sets you on the default road, sort of. Yeah. There's nothing wrong with like, I think there is a thing to be said for giving the player familiarity, like, Oh, you know, this physics is just like I expect, you know, but yeah. So like, I think you want to like keep safe with some things and experimental with others so for sure. I'm having a little flexibility I guess. Yeah, yeah. For sure. I think we can move on to some of the audience questions here. So one question was should I always try to optimize my game for file size? Yeah, so I think this is a great question. One thing I know in my talk, it was like on a lot about, you know, you can optimize this and that, and you know, all of these different things.
Optimization and Priorities
There are cases where optimization may not be necessary, such as coding games where users need to analyze the code. However, in general, optimizing each aspect of the game is beneficial. Prioritizing file size is important for web releases.
But I do want to hammer home that like, there are definitely cases where you may not want to optimize like maybe it's a coding game, like a game about coding. So you actually want the users to dig into your code, see how the enemy logic works and use that to like counteract them. So if you're minifying all that code, it's like, you know, poor users, game unplayable. But I mean, in the general case, I think, you know, like if you take each little optimization, you can generally, you know, even if it's a big game, each little optimization is definitely a win. But it's really all about managing priorities. Like are you going for small games like or do you have other priorities of making it more realistic? I think this talk and I wanted to give this talk to everyone is I do think file size is an important value if you're planning to release to web. If you know, you don't value being able to jump in and out really easily and thus don't care about file size, like maybe the web isn't the best platform, you know, if you're going for the web might as well make the best use of that.
Retention, Reach, and Testing Internet Connections
Having a smaller file size helps with retention and reach. Publishing a web version, especially if optimized and loads quickly, can attract more users. Even if the game doesn't load faster, making it look like it's loading can reduce the perceived load time. Perception is more important than the truth. Testing slower Internet connections can be done using browser tools that limit network speeds.
I think that I think that makes a lot of sense. And I think it was something maybe you mentioned in your talk about how it. It'll generally like having that much smaller file size just helps in terms of retention or reach right like people are more likely to stay on on the game. And for me at least that's part of the motivation of the web if I'm if I'm deciding whether I'm doing project on desktop or not.
A lot of times I'll tell like my friends like, oh, well, you could make this in that publish on desktop but you're probably gonna get 10 times as many people looking at it. If you publish a web version and especially if it's something that is optimized and loads quick and you can just send someone a link to play.
Yeah, for sure. Like I know in our metrics, like I can't share the exact one. So we've definitely seen that a lot more people will jump in not just when it's a web versus mobile, but also when it's like small web versus big web or, you know, web that lets you at least start playing versus web that requires sign up before you can even, you know, hit start. It makes a lot of sense. Yeah. Oh no, keep going. Sorry. I was going to say, I think some of the advice you had in the talk too, it doesn't necessarily even have a trade off. Like one of them was like, you want it to load faster, but even if it doesn't, you can make it look like it's loading, like something's happening. So you don't even need to make it, you know, you don't need to throw away art assets, but just communicating that to the player, you know, helps, you know, visually delay, reduce the perceived load time, which I think is pretty valuable.
Exactly. Like, I think one thing that I think it was a professor actually told me, but I think it's really valuable, is like the truth versus like perception. It's like perception is actually more important because like the truth, it's like no one knows actually what's happening, but if the user feels like the truth, like what the user perceives is their truth. So that's really what you want to optimize for. I love that. I feel like that's a great takeaway. A very succinct great takeaway. We had another question about when you're developing. How do you test like slower Internet connections?
Sure. This is a great question. We have like a variety of ones. I think for the quick test, like, you know, I think the important thing is feedback loops, right? You want to keep the feedback loop just in general, you know, if you're making art or want to shrink that down as fast as possible. So you can just keep iterating on it. So for quick tests, like the Chrome browser tools or you know, Firefox or whatever, where you can like limit network speeds, I think that does a great rough job of that.
Testing Tools and Accurate Simulation
There are tools available that can simulate different locations, networks, browsers, and devices to help you accurately test your game. These tools provide a more physical and accurate experience, complementing the Chrome developer tools.
But for you know, that helps you get into the right position when you really want to make that precise cutoff, there's a lot of tools these days. I don't want to like sponsor any exact one, but where it will actually like proxy your through a server so that you can see how it actually feels if you live in different locations or through different networks, or different browsers, devices, which I think is really cool. That's awesome. I hadn't thought of that, because my goal has always just been the Chrome developer tools. But I like that, you know, having having this extra tools and we would have like, again, I think that gets you like most of the way there. And when you really need to figure out which one to go with, I think getting it on a more physical more close to the metal, I think feels more accurate.
Comments