Sun. Apr 11th, 2021

New Jacks

GET JACKED IN!!

How I made a 3D game in only 2KB of JavaScript

6 min read
Months ago, when I heard that the legendary JS1k game jam would not be continuing, I talked it over with some other devs and decided to help fill the void we would host a 2k game jam on itch called 2kPlus Jam. The primary goal of this comp was to create a game that fits…
How I made a 3D game in only 2KB of JavaScript

Months ago, when I heard that the legendary JS1k game jam would not be continuing, I talked it over with some other devs and decided to help fill the void we would host a 2k game jam on itch called 2kPlus Jam. The primary goal of this comp was to create a game that fits entirely in a 2 kilobyte zip file. That is incredibly small, for point of reference a 3.5 floppy disk could hold over 700 of these games.

My entry, Hue Jumper, is an homage to 80’s racing game rendering technology. The 3D graphics and physics engine was implemented from scratch in pure JavaScript. I also spent ungodly hours tweaking the gameplay and visuals.

The theme for the game jam was “Shift” which I incorporated by shifting the hue for the world’s color when the player crosses a checkpoint. I imagined that crossing a checkpoint was fictionally like shifting or jumping into a new dimension with a different hue, which is how I came up with the name “Hue Jumper”.

This post is going to be a bit long because it contains the JavaScript code for my game in it’s entirety. The code is already well commented so I’m not going to explain every line of it nor are you expected to read through all the code now. Instead my goal is to explain how it works, why I made it this way, and walk you through the overall structure. This same code is on CodePen for you to play around with live. So please continue reading, buckle up, and hold onto your butts!

Inspiration

Out Run by Sega

My primary inspiration comes from nostalgia of classic 80’s style racing games like Out Run. Using a similar technique, they were able to push real time 3D graphics on very early hardware. Also I have recently been playing some modern racing games like Distance and Lonely Mountains: Downhill which helped inform the visual design and feel.

Jake Gordon’s project to create a pseudo 3D racer in JavaScript was a big help. He wrote a fantastic multi post blog series that explains how it works. Though I started from scratch, seeing his code helped me work through some of the math and other problems I encountered.

I also looked at a JS1k game called Moto1kross by Chris Glover. This simple one kilobyte racing game helped give me a point of reference for what was possible, and with an extra kilobyte of space available I knew I had to far surpass it.

High Level Strategy

Because of the strict size limitation, I needed to be very careful about how my program is structured. My general strategy was to keep everything as simple as possible in serving the ultimate goal of making a game that looks and feels solid.

To help compress the code, I ran it through Google Closure Compiler which removes all white space, renames variables to 1 letter characters, and preforms some light optimization. You can use this site to run your code through Closure Compiler Service online. Unfortunately Closure does some other stuff that doesn’t help, like replacing template strings, default parameters and other ES6 features that help save space. So I needed to manually undo some of that and preform a few more ‘risky’ minification techniques to squeeze out every last byte. It’s not a huge win though, the bulk of the savings comes from the structure of the code itself.

The code needs to be zipped to fit into a 2 kilobytes. If that was not an option there is a similar yet less powerful tool called RegPack which can make self uncompromising JavaScript. Either way the strategy is the same, to repeat code wherever possible and let the compressor deflate it. For example there are certain strings which appear often so their compression ratio is large. Some of the best examples are c.width, c.height, and Math, but there are many other smaller ones that add up. So, when reading through this code, keep in mind that you will often see things purposefully repeated to take advantage of the compression.

CodePen

Here’s the game running live on CodePen. You can actually play it in the iframe, but for best results I recommend opening it in a new tab, where you can edit or fork the code.

HTML

There is very little html used by my game, as it is mostly JavaScript. This is the smallest way to create a full screen canvas, combined with code that later sets the canvas size to the window inner size. I’m not sure why on CodePen it was necessary to add overflow:hidden to the body, but this should work fine when opened directly.

The final minified version uses an even smaller setup by wrapping the JavaScript in an onload call… However, during development I prefer not to use that condensed setup because the code is stored in a string so editors can’t properly highlight the syntax.




Minification

That’s the entire game! Here’s a look at the final result after it has been minified with color coding to show the different parts. After all that work, you can only imagine how satisfying it is seeing my entire game in a small patch of code like this. This is also before the zip which cuts the size almost in half by eliminating repetitious code.

  • HTML – Red
  • Functions – Orange
  • Setup – Yellow
  • Player Update – Green
  • Background Render – Cyan
  • Road Render – Purple
  • Object Render – Pink
  • HUD Render – Brown

Caveats

There are other ways to achieve 3D rendering that provide both performance and visual benefits. If I had more space available, I would have preferred to use a WebGL API like three.js which I used for Bogus Roads, a slightly similar game I made last year. Also, because it is using requestAnimationFrame, there really needs to be some extra code to ensure the framerate is capped at 60 fps, which I have added to the enhanced version. I prefer using requestAnimationFrame over setInterval though because it results in smoother rendering due it to being vsynced. One major benefit to this code is it is extremely compatible and should work on any device, though it is a bit sluggish on my aging iPhone.

Wrapping It Up

Thank you for reading all of this, or at least scrolling to the bottom! I hope you learned something new. If you liked this post, follow me on Twitter for more stuff like this. Also, there are more mind blowing games made for 2kPlus Jam, you check them all out on itch!

The code for this game is open source on GitHub under the GPL-3.0 so feel free to use it in your own projects. That repository also contains the 2k version which at the time of posting is only 2031 bytes! You can also play the “plus” version with some bonus features like music and sound effects.

I will leave you with the first tweet I posted about this game with an early video…

I've been working on a tiny racing game from scratch in javascript for #2kPlus jam! ?

Mostly finished the core rendering tech now, it is super smooth and tiny. Today I move on to gameplay.#javascript #creativecoding #gamedev #codegolf #tinycode pic.twitter.com/8jNdscD295

— Frank Force (@KilledByAPixel) February 11, 2020

And of course, I made a promotional dweet to celebrate the game’s release…

Hue Jumper Promotional Dweethttps://t.co/wQ4E7ma2ke

for(c.width|=i=300,x.translate(980,9);i–;x.fillText(r?`___${a%43>9?'_':' '}___`:'u25B2',r?-19:S(a*a*199)*49,8))x.scale(1.02,1.024),x.fillStyle=`hsl(${i+((r=(a=i+t*60|0)%9!=0)+t|0)*60} 69%40%`#javascript #2kplus #tinycode pic.twitter.com/gQ8sylp1N6

— Frank Force (@KilledByAPixel) February 21, 2020

Read More

4 thoughts on “How I made a 3D game in only 2KB of JavaScript

  1. Pingback: 주소모아
  2. Pingback: Replica Panerai
  3. Pingback: sexybaccarat

Leave a Reply