So I participated in js13k again this year, this time sticking to my cards better and decided to make an eye- as much as a brain-bending puzzle game called Edge Not Found. After working for almost a year in TypeScript (and Canvas) for my job, I feel I have a lot more experience working with the browser, along with the things I’ve learned from my entry last year. So here’s another larger-than-13kb postmortem about a smallen-than-13kb game.
js13k last year was the most fun I’ve had with a game jam. There’s a lot of activity from devs on Twitter (though not overwhelmingly so), the month-long deadline is very relaxed compared to some other game jams (you’ll finish on time even if you contribute just half a kilobyte per day!), and there are simply some amazing technical marvels that people create even despite all these limitations.
And, of course, a seed for a game idea. I’ve made a couple Sokoban variants before, and my next idea was to have the level continue infinitely into all directions. A small spoiler warning, though: I’ll vaguely describe some of the puzzle mechanics (though no puzzle solutions!) in this postmortem, so you might want to play the game a bit before reading on.
A level in this game is defined with a 2D array to set up the grid and some metadata such as the level name, but also shift offsets that are used for later levels. I think undo is the most important functionality any puzzle game should support, and as such I built the game up in a way where the game & rendering logic are separated to make that possible.
The rectangular border in the middle of the screen indicates the only part of the level that actually exists. If you walk through the border, as far as the level state is concerned, you’ve wrapped to the other side of the level. There’s some math to decide where you should end up if the level is “shifted” (the level grid is not uniformly aligned).
One thing I attempted to implement was pathfinding. Lately, a number of Sokoban-style puzzle games have popped up that allow players to click or tap a location and have the character figure out how to walk over there. I really wanted to have this feature, but A* pathfinding was not intended for infinite grids. I really want to implement this for my next puzzle game, though.
Make no mistake: even code that fits on a floppy disk can be incredibly slow when drawing a lot of things to the screen. At first I drew a rough.js primitive for every block on the screen, which is where this limitation became evident. Then, I drew the shapes to a level canvas, which is then drawn as an image and repeated across the screen. This was better, but still slow on levels with a lot of objects. So then I also drew all individual objects to their own canvases, drew those onto the level, then drew that repeatedly on the main canvas. Also, the game now attempts to skip rendering when nothing changes, using the object-level-main canvas technique described above, and that caused a more significant improvement than I originally expected.
A nice benefit is that all of this makes it really easy to support multiple color palettes, too. I made a couple of nice ones, including one based on Okinaki (my favorite color theme in N++) and BackFlipped, a theme with the color palette from my previous entry.
Making puzzles for this game took a lot of iteration, but overall I’m really happy with the level of puzzle quality in Edge Not Found. I iterated a lot and kept the very best levels, and made sure to enforce the intended solution. In world 1 and 2 you’re still getting used to the wrapping world and the puzzles are easy: the wrapped grid makes sure that the possibility space is bigger than in an ordinary version of Sokoban because it’s easier to retrieve blocks from tight corners in this game. But everything from 300 onward showcases very interesting properties of the system that are really fun and mind-bending to discover, and in some cases allow for very hard puzzles. I am still a bit divided on the new mechanic introduced in the final world, which I would preferably have introduced earlier, but since it mostly exists to compliment the other mechanics in the game, it does make for a fitting finale of the game together with level 404.
I especially love how the level select came together. Initially, I was planning for the [Esc] menu to be used for navigate between levels, but procrastination hit me hard. I ended up implementing it as a regular level instead, allowing me more control over in which order the player could play the levels.
Initially, all levels could be accessed at the start of the game. I decided to gate it slightly, to make for a smoother difficulty curve and a better sense of progression. You don’t have to beat all of the levels to continue, since some of them are really hard and I want players to have a realistic chance to unlock level 404 and finish the game. The main problem that spawned out of this is that the level select would become so big, it would no longer fit on the screen. That’s how I ended up with the current tight design, which already teases the mechanics of level 200 a little bit if you pay attention.
I had a blast making this game, and the reception also seems a lot better than for my entry last year. It was a great test run for my mini framework Tomato, and I worked out a lot of the kinks for the first game using it. I’m certainly looking forward to doing more projects like Edge Not Found, and I’m really curious to how it will do in the js13k voting.