Another JS13k jam: Lessons learned

As its name suggests, the JS13k game jam is about writing a game in Javascript sized under 13kb when zipped. This jam is the little brother of the gamedev.js game jam but I like it a whole lot more since the size restriction makes the game more “pure”. The former allows for web exports (from unity, gotdot, etc) and such which, personally, I feel takes a lot away from the JS part of the title.

The game

This year marks the 13th edition of the JS13k gamejam and as such the theme was, appropriately, Triskaidekaphobia (the fear of the number 13). So many possibilities…

My entry is called 13S (https://js13kgames.com/2024/games/13s) and it is an action platformer inspired by speed-runners. The objective is to go through the levels in under 13 seconds.

The game placed 72/171. I’m a bit disappointed. I thought the game was rather polished. On the other hand, this really makes me think about the elements that make for a good game. It isn’t the technical aspect, it isn’t the graphics, it isn’t the audio, it isn’t the gameplay, but rather all of the above combined into something that is FUN. That is not a simple thing to do. More than disappointed, I am inspired to try again! Ultimately, this jam (or all jams) is about constant improvement.

The tools

What went right

I tried my best to reuse code. This turn out to be a HUGE time saver! I wanted to try ECS again and was able to reuse my previously developed ant-ecs architecture. I had used this for my previous game Angry Temujin. This time I didn’t have to spend a single second improving it or fixing bugs. It just worked! It is not perfect, and it is not pure, but it is tiny and it works.

Most of the systems I rewrote to match the need for this game, but they were surprisingly similar to stuff I had before. Most of the code ended up being recycled from my old code.

Notably, the rendering system (a canvas renderer) needed a lot of rewriting, but it was great to see that the things that were working fine didn’t need any additional thinking. It was only the features I wanted to add that needed work. In the future, I’ll focus on a WebGPU renderer instead. WebGL is on it’s way out and the canvas renderer is super slow.

Even though the ECS architecture I put together is not super optimized, it will handle many thousands of objects just fine. Painting those objects however, is just painfully slow. A self-assessed negative I had on the rendering system is that it didn’t use matrices for the transformations, which isn’t wrong per se but it is a bit crude and brute-force-ish.

Another win: the build tool. It takes the project and compiles with esbuild. Then the code is passed to UglifyJS and to roadroller after that. Finally, it is zipped and displays a message on how big or small the zip file is. It is so simple, but putting it together took a fair bit of time. Not having to worry about it was absolute bliss.

Lesson learned: reuse code as much as possible! With ECS, if the components are done right, and the systems are done right, many of them can and should be easily reused without changing a single line of code.

What went wrong

This part is very painful. It hurts just to think about these things as they could have been prevented, but there in lies the lesson: I made the decisions. These mistakes were made by me and me alone. I knew some of these things could have been better, yet I decided to take a different route.

Horrible thumbnail image

I wanted to go for an old-school game cover art where the graphic doesn’t match the game at all but it gives you a sense of what you will do. Consider these two classic NES beauties:

The cover has absolutely nothing to do with actual gameplay! So I came up with this garbage-infused-AI-powered-dumpster-fire:

At first sight it is nauseating, but you can kind of see the intention. However, when you place it in context with the other games it just disappears:

I CAN’T FIND MY OWN GAME! Everything about it is bad. This is so sad. Even now, as I write this and know the game is on that screenshot, I still can’t find it! If I can’t find my own game, there is 0% chance other people will be enticed to try it. I firmly believe that if more people had tried the game, and the other issues below were fixed, it would have scored significantly higher. Actually, I think this is a lesson for all devs. Mobile app developers know this pain very well, and I should have known better: in a sea of apps and icons (thumbnails in this case), if yours doesn’t stand out, it will not be clicked … ever.

Add video faster

I don’t remember if this was a possibility last year, but this year you could add a video of the game to the entry. This is huge! A lot of people complained (rightfully so) that the game was too difficult. This could be mitigated a bit if I had uploaded a “walkthrough” video. I did upload it, but by the time I did it was way too late. The voting deadline was only days away. This was a big mistake. I should have done that video sooner. It was so easy to do too! Maybe a walkthrough would have been too much and maybe it would have triggered a “boo now i know what to do” reaction, but at least it would have shown that the game isn’t that difficult, you just don’t know what to do yet. At minimum it is an opportunity to show the game to people that don’t have time or patience to play it! This was a big miss, on such a low hanging fruit too.

No playtesting

It is so easy to ask someone to just test your game. I didn’t. I have all kinds of excuses why, but ultimately, I could have and didn’t.

This cannot be any more obvious than it is on the first level. As you start the level, spikes start crashing down from the ceiling … slowly. You have 13 seconds to get through the level before the spikes kill you.

There is (fixed on the director’s cut) an oversight from my part in level 1 where if you die too fast, there is not enough time for the spikes to “reset”. When you restart you run right into level 1, where the old spikes are already low and kill you instantly. My dumb brain thought that between the death animation and the time it takes to get to the level the spikes would have cleared. This was stupid. I ran into these myself! The very first person that tested the game (after it was released) complained about this. It made the game unplayable! On a scale from 0-10, 10 being catastrophic, I’d qualify this as a 13 (pun intended).

Make the premise more obvious

The game scored fairly low on the theme metric even though I think “13 seconds per level” was rather on-point. But this one is on me (again). I wanted to do 13 levels, 13 seconds each. Unfortunately, I didn’t have enough time or creativity to do 13 levels, but I did do 7 levels, at 13 seconds each. The problem is that I didn’t drive the 13s counter down! When you play the game, you just know you have to run because the level is working against you. But nowhere it says or shows that this is a matter of time! As you run through the level time is running out! I wanted to do, inspired by doom’s push forward combat, a game where the stage forces you to move forward, and I think I did achieve that, but in doing so it watered down the jam’s theme … not good.

Difficulty not adjusted

Courtesy of not play-testing, the game was just too hard. A lot of people complained about this. It is insta-death by design, but sending you to the very beginning was just too punishing, specially considering the bug/oversight describe above. Difficulty cannot be adjusted based on what I feel because I played the game more than anyone and I’m 100% not a game-balance-guru.

Not enough music or sound effects

One of the things that really made last year’s entry drop was the lack of audio. I wasn’t going to fall for that again, so I added some sound effects, and a bit of music courtesy of ZzFXM (and ZzFX). The sound effects are decent, but could be better, there could be more of them. I simply ran out of time. Same goes for music! It is a difficult part of any game, and it is very time consuming to find or create the right soundtrack. Even putting it like that sounds overly simplistic. A game is a world, what does that world sound like? What are the little things making noises that are part of the ambiance but not of the gameplay?

Simply slapping some sounds on the game is not going to work, as it clearly didn’t even though I did put a lot of love in creating them.

ECS done wrong

I am not an ECS guru by any means, I simply created a library that works, and works for me. That said, the idea is to go as pure ECS as possible. This is not what ended up happening. I added a lot of global state, the systems have a bunch of callbacks and event listeners (a sign that things are going awry). Part of it is that when crunch time kicks in, JS let’s you get away with all kinds of things and will still work!

Ultimately, I’m thinking of it as a hybrid-ecs system. Hell, even Unity uses a hybrid system. When I look at it like that, it doesn’t feel as bad.

Where is the juice?!

I really wanted to add some juice to the game. Camera shake, ease-in or out transitions, etc. I just didn’t have time for it. The main game is more important. Or is it? I highly recommend watching The art of screenshake, where a prototype platformer is turned into what feels like an awesome game just by adjusting a few things and adding “juice”. Yeah, juice matters.

Conclusion

There is nothing wrong with these mistakes. Improve, evolve, try again.

What is good audio? What is good gameplay? What are good graphics? What are good controls? I don’t know … I just don’t know. But one thing is for sure … it feels right and it feels fun.