Mongol March: Navigating the JS13k Challenge

The post-mortem of the Mongol March, the entry of JS13k 2023 competition.

Leo Kuo
10 min readSep 23, 2023

Mongol March is a tower attack game based on Tetris-like blocks. As a 13th Mongol Empire’s commander, your duty is strategically arrange the blocks to summon Mongol soldiers and lead them to victory.

Play game here: https://js13kgames.com/entries/mongol-march
Give me some feedbacks here: https://dev.js13kgames.com/2023/games/mongol-march

Before JS13k 2023 began

Two months ago, I knew nothing about JS13k games. It was because a colleague noticed that I was developing games (puzzle game, baseball game) and informed me about this interesting competition. JS13k is a competition that imposes a constraint where every entry must be under 13kb after compression. Participants are not allowed to use external libraries or services.

At first, hearing about this restriction seemed incredible to me because based on my past experience in game development, a single sprite image could easily exceed 13kb. After looking at the entries from the past few years of JS13k, I found it even more unbelievable. It’s hard to imagine how these games are compressed to fit within 13kb. This sparked my curiosity, and I wanted to give it a try to see what the experience of creating a 13kb game would be like.

Ideation

Theme: 13th century

It’s actually quite a challenging theme because it requires a moderate connection to history. I tried to find some information about the 13th century history and discovered that the rise of the Mongol Empire was a significant event during that time. When you talk about the 13th century, you almost always mention the Mongols, so I started brainstorming with the Mongol Empire as the central theme.

Using past entries as benchmarks

As a first-time participant in JS13k, I want to be mindful of my capabilities while brainstorming. This is to prevent the scope of my idea from becoming too unrealistic, leading to the risk of not being able to complete it or exceeding the size limit. So I used past JS13k entries as benchmarks for determining the scope of my project.

In the end, I chose Dan Price’s work from last year, “Norman the Necromancer”, as a reference. This game features fantastic visuals, uses simple mechanics but very addictive, and provides a great gaming experience that immerses players into the flow state. It’s exactly what I want in my game!

Brainstorming gameplay ideas

Due to the theme being focused on the Mongol Empire, I initially thought about a war-themed game. My preliminary concept is similar to a traditional Chinese chess-like board game. Players would summon and place soldiers on the board to engage in battles with the opponent.

The cards below are soliders, defined the speed, occupied blocks and movement.

However, I quickly realized that this idea might not work. My girlfriend and I simulated it using a paper prototype and discovered its limitations. The biggest drawback was that there are already many games of this type, and adding new concepts would likely push it beyond the 13kb limit. So I wanted to expand on this idea with two goals in mind:

  • Reduce the scope
  • Add innovative elements

The final concept

During the process of placing soldier chess pieces on the paper prototype, I had the idea that if we allow players to plan their strategies in advance and then simply deal with the war scenes later, the scope and concept would become much simpler.

Furthermore, the concept of “placement” reminded me of the board game Patchwork. In this board game, players need to strategically place patch pieces onto their grids, and leaving empty spaces results in point deductions.

Finally, I came up with an idea of main game loop:

  • Place blocks into grid:
    There will be an empty strategy board at the beginning. Players should make decisions to how to arrange the soldiers.
  • Start summoning soldiers to fight:
    According to the strategy board, summon soldiers to march, attack enemy soldiers, and siege the castle to achieve victory.
  • Rewards and penalties:
    Upon victory, players can choose to receive a power-up reward. Additionally, any unoccupied grids in the strategy board become locked, serving as a penalty and hindrance to the player.

Game engine and tools

I used to be accustomed to using Phaser 3 for HTML5 game development. However, due to the size constraints, I chose Kontra.js because it has extensive documentation, and I believed it would allow me to quickly learn how to use it. Additionally, Kontra supports finer-grained control than tree shaking, allowing you to remove unused parts of classes during bundling. This can be quite helpful for size optimization!

As for bundle tools, very thanks rob_louie for creating js13k-typescript-starter. My vite config was mostly based on Rob’s settings. With these configurations, especially RoadRoller, the bundle size was significantly reduced. It’s amazing to have these tools to help, and they are truly a lifesaver!

Graphics

For me, I’m accustomed to creating some concept art first, as it helps me better envision the final look of the game. I find that this approach greatly assists me in the game development process.

I initially wanted to try pixel art, so I visited Lospec to find color palettes. Although in the end, I opted for the more familiar vector art…

In fact, I initially thought I should use pixel art to reduce the size, but since I only know how to create vector art, I had to work with my existing skill set.

PNG -> SVG sprite -> SVG

When I initially tried placing PNG images into the canvas after creating the character, I quickly realized that the quality was poor, as the edges of the images became pixelated.

So, I changed my approach and used SVG. I made some adjustments to try to reduce the SVG size further.

  • During the drawing process, I minimized the number of anchor points
  • Merged blocks of the same color together whenever possible
  • Use SVGOMG to optimize and minify svg file
Minified by SVGOMG, chose minimum precision

It initially looked good, but when I imported it into the canvas, I encountered an issue. When using an SVG sprite, the default image sizes were set to 300x150, which made it very challenging to calculate image scaling and positioning. Additionally, using an SVG sprite resulted in image output, which prevented me from using RoadRoller for compression.

So in the end, I wrote the SVG paths in the script and generated images through code. This way, the SVG paths were encoded and compressed by RoadRoller.

const SVG_DATA = {
icon1: 'width="16" height="16" viewBox="0 0 16 16"><path fill="#4B726E" d="M4 2v10l4 3 4-3V2H4zm6 6H6l1-1-2-2h2l1-1 1 1h2L9 7l1 1z"/></svg>',
icon2: 'width="16" height="16" viewBox="0 0 16 16"><path fill="#4B726E" d="M10 1v2-2h2v5h-1l1 9H4l1-9H4V1h2v2-2z"/></svg>'
};
const imgContainer = document.getElementById("imgs");

Object.entries(SVG_DATA).forEach(([id, data]) => {
const encoded = encodeURIComponent(
`<svg xmlns="http://www.w3.org/2000/svg" ${data}`
);
imgContainer?.insertAdjacentHTML(
"beforeend",
`
<img id="${id}" src="data:image/svg+xml;utf8, ${encoded}" />
`
);
});

Next time, I might consider using the canvas context to directly draw shapes. This approach perhaps more space-efficient.

Soliders

When handling the display of soldiers, the general principle is to reuse assets wherever possible. For example, the bodies of soldiers can be the same for each type of soldier. This approach helps to optimize and reduce the overall size of the game.

I created an abstract class called “BaseAttackUnit” to define some basic methods and properties for combat units. This allows various combat-capable units, such as infantry and castles, to inherit these methods and attributes.

For animations, I opted for the simplest approach. For example, in the case of walking animations, I used a straightforward hopping motion. This simplicity can be quite effective in reducing file size while still conveying the necessary actions.

Tetris-like block and boards

There are two types of board in the Mongol March. One board is used for player placement, while the other is dedicated to displaying the game. Both boards are composed of grid units, with each grid having two layers. The lower layer represents the background color, while the upper layer is used to display the color of block.

When the mouse hovers over a grid on the strategy board, the game calculates, based on the current block’s shape, which grids need to change color, and then performs the rendering accordingly.

Summon soldiers to attack

When the player finishes placing the blocks, a timeline will appear on the strategy board, scanning grids from left to right. Depending on the type of block placed, different types of soldiers will be summoned.

On each grid, there is stored information such as the block’s unique ID, soldier type, and additional details like whether it has been scanned or blocked. Utilizing this information allows for displaying different grid states and emitting events for summoning soldiers based on the stored data.

Due to concerns about performance, I implemented logic for reusing soldiers. Every time a soldier needs to be summoned, the game checks if there are any “dead” soldiers available and “revives” them to be placed back on the front lines. This approach can help optimize performance by reducing the need to create new soldier instances constantly.

Final Steps

Once I had implemented all I mentioned above, I realized that the file size had already reached around 13,200 bytes. However, there are still some pending features that I haven’t implemented yet, including:

  • Level rewards
  • Perfect placement reward
  • Score display
  • Music and sound effects

I attempted to use zzfx but realized it was too late, and adding music and sound effects would require an additional 500 bytes of space! So, in the end, I had to make the painful decision to cut music from the game…

Level rewards

Level rewards involve a mechanism where, after conquering a castle, players can choose whether to enhance their abilities. However, in exchange, the enemies will also receive a slight boost in strength. This is also the only opportunity for players to potentially unlock locked grids on the strategy board.

Perfect placement reward

The game encourages players to optimize the placement of units on the strategy board, which is the core of the gameplay. Players need to think about when to summon which type of unit for maximum effectiveness.

To reward players for perfect arrangement, when they achieve a flawless setup on the strategy board, a powerful Khan is summoned for battle. This design allows late-game players to have a chance at a comeback victory!

Summon Khan! (the large one XD)

Score display

The scoring is calculated based on “battle units killed per wave” + “remaining battle units per wave.” The abilities of the units are also translated into scores, allowing the rewards for each round to be converted into points. With the improvement of unit abilities, players can earn more points per wave.

Finally, I finished!

In the end, I found that the file size was still larger than 13,312 bytes even after compression. So, I made adjustments to remove unnecessary text and increased the compression level with RollRoader. Finally, I submitted my first JS13k entry!

However, when I had my colleagues play the submitted game, I found a problem. The game lacked clear instructions and wasn’t very intuitive, which led to my colleagues having to figure out how to play for a while. Since I couldn’t make substantial adjustments to the game at that point, I decided to write a clearer game introduction and also created an instructional video.

Afterword

I found the process of participating in JS13k to be incredibly enjoyable. Not only I learned a lot from game development. What’s most valuable is that fellow developers in the competition are always willing to offer advices, and the organizers are actively involved in fostering the community.

When I saw the positive reviews from everyone on the voting website, it was incredibly fulfilling. Additionally, everyone provided insightful feedback, which was very rewarding for me! The next step for me is to port the game to mobile platforms and develop it further to create a fully polished product for publication.

If I participate next year, I will likely try the following:

  • Directly use the canvas context for drawing graphics.
  • Consider music and sound effects as essential parts of the game experience.
  • Design a well-crafted in-game tutorial, as it significantly affects the game’s overall experience.
  • Allocate time for play testing and gather player feedback to improve the game before submission.

Thank you for watching! Here’s the GitHub repository for my game. Remember to give it a try, watch the introduction video I’ve created, and if you could provide feedback, I would greatly appreciate it.

--

--

Leo Kuo

Indie Game Developer | Frontend Developer | iOS Developer | UIUX