Things learned from developing an action game in JavaScript

A.E.Veltstra
CodeX
Published in
5 min readJul 13, 2023

Every game dev should clone Tetris. Just call it something else: the name Tetris is trade-marked.

Back in 2014 when I had too much time on my hands, I implemented a few video games as part of my professional portfolio, but also as a way to let my kids play video games without all the advertisements and in-game money-grabs. One of them is a Tetris clone that I called Trezjr. It’s still available online, free to play. All you need is a web browser that runs JavaScript and supports HTML5 Canvas.

“Trezjr” as a pun on both Tetris and treasure employs state-of-the-art JavaScript wizardry, game mechanics, audio and video, accessibility and usability, and loads blazingly fast even on mobile phones.

Why does it load fast?

  • Minimized and minimal source code for speedy downloads,
  • No bloated frameworks,
  • Not loading any audio assets your browser doesn’t support,
  • All graphics are either stupidly light-weight or generated by the web browser,
  • No custom fonts and no web fonts,
  • No tracking scripts,
  • No advertisements,
  • No social media button scripts,
  • And the web site used to be hosted via a CDN which made it even faster.

No frameworks?

Well, not entirely. There is 1: zjrJS. I built that specifically to deal with browser inconsistencies. It’s atomic, lightweight, and minimized. It has no outside dependencies. Its API uses short method names most of the time, which helps minimize implementations.

No Advertisements

Ads commonly get pulled into web pages using javascript. Common ad hosts like DoubleClick have done their best to optimize their scripts and serve their content speedily. If you were to look at the network loading console of a game info website like IGN you will notice a barrage of code and media dedicated to advertisements. All of that costs time and precious data. To keep Trezjr fast, it shows no ads at all.

No Social Media Button Scripts

Wait… but the web page does show social media sharing buttons!

Yes, it does. I made those. They are not javascripts pulled in from some other website, that bring megabytes worth of SDKs, analytical and tracking tooling, and stupidly heavy imagery. No: these are HTML buttons in an HTML form, that I created.

Graphics generated by the browser

Using JavaScript to draw images onto the Canvas HTML element allows us to create wonderful imagery without forcing the user to download the megabytes needed for bitmapped media. MDN has a great tutorial on how to use it.

Trezjr uses 3 Canvas elements, 2 of which lay on top of each other: the bottom shows the grid while the top shows the puzzle pieces and lines. An asynchronous video loop redraws the puzzle pieces twice a second. It is asynchronous because a different game loop observes and reacts to user interaction, and a 3rd game loop keeps track of score and overall game-play speed.

Only Loading Supported Audio

Trezjr uses the Audio HTML element. That lets us specify multiple types of audio resources. If your browser supports the smallest audio type, you receive that. In Trezjr, that’s Theora/Ogg. Otherwise it gives your browser the opportunity to use mp3 or wav. Wav format is the heaviest, and chances are your browser supports one of the lighter formats.

You won’t see the Audio HTML element in the source code: it gets loaded on demand, if you choose to activate sounds.

Audio Sprites

We took great care to work with audio sprites: like image sprites it is a larger audio file that contains small bits of audio. Thus instead of the browser having to load multiple media files, it loads just 1. Whenever a sound happens, the coding knows which seconds from that audio file contain it, and have the Audio HTML element play it.

Trezjr allows for multiple sprite files with specifications of which sound to find at what times, but it turned out the game worked fine with only 1 file. This is a clear case of YAGNI: a system was created to house more than needed. YAGNI is a principle in software engineering, that prevents developers from spending too much time on a feature.

The Game Script is an Executed Object

Benefit of this approach is that the script cannot start working before it has finished loading into the browser. That is exactly what is needed, given the fact that all the game’s source code is contained in a single script, and all of it is required to be available.

Another benefit is that grammar mistakes become apparent immediately: the browser won’t be able to interpret/compile the object, and nothing will run. That means no visible grid of columns and rows, no falling puzzle pieces, no audio, no game-play.

That benefit also is its drawback: a single misplaced character will break the script. For larger applications that have more lines of code, that leads to unacceptable maintenance activity. One that can be prevented by either employing a really good IDE, or by breaking up this single executable object into stand-alone functions.

The lesson here is another principle of programming: separation of concerns. Even though all of the code is required to play the game, a single misplaced character shouldn’t cause the entire game to malfunction. Without it, triaging for the location of the bug becomes quite difficult.

User Actions Update Puzzle Pieces Asynchronously

When a gamer presses buttons to move or rotate a puzzle piece, its location and rotation do not update immediately. The input loop is separated from the video display loop. Since the video display updates only twice a second, it is very likely that a user will submit multiple actions before their effect becomes visible. The only action that updates the display immediately, is the sudden drop.

This could present a problem with gamers who expect immediate feedback. If the display could be updated at a higher frame rate, the game would feel much more responsive.

With its current architecture, Trezjr cannot make that happen. That’s because the video frame rate is tied to the speed at which the puzzle pieces fall down. This should be decoupled: a separate loop should determine piece location, while a dedicated loop draws video.

Conclusion

While it was fun and very educational to build this game, it’s even better to look back and identify parts that should or shouldn’t be replaced. Keep your old projects available for educational purposes.

--

--

A.E.Veltstra
CodeX
Writer for

Makes software better. Easier to use, faster to run, cheaper to maintain. Married, has kids, likes making music and climbing rocks. Patreon: @aev_software