A Tourist In Paris: a JS13KGAMES 2017 postmortem

Jerome Lecomte
11 min readDec 3, 2017

--

This was my second time participating in the JS13KGAMES challenge, a month long competition running from August 13th to September 13th about creating a HTML5 game that fits within 13 kilobytes when zipped (think game jam mixed with code golfing).

This year, I made A Tourist In Paris, a game about the anxiety of being lost in an unknown city (official JS13KGAMES entry at http://js13kgames.com/entries/a-tourist-in-paris or on my website http://herebefrogs.com/a-tourist-in-paris/).

The tiny blinking square is a tourist, and the big blinking square a monument…

Don’t forget to also check out the post mortem for Blade Gunner, my entry for last year’s challenge.

Game ideation

Ideas didn’t come easy this year. The theme was “Lost”. I search for inspiration in a list of expressions containing this word and it all seemed to boil down to two things: either something misplaced needs to be found or you’re the lost one who needs to find your way back (the later arguably just a special case of the former). So much for an epiphany… Brainstorming some more, I recalled Shigeru Miyamoto stating his inspiration for Legend Of Zelda came from his explorations of the hillsides surrounding his childhood town of Sonobe, Japan. My childhood explorations were set in the streets of Paris, France, so I imagined the tribulations of a tourist lost in the City of Lights, desperately searching for the monuments he wants to visit before his tour bus departs for the next European capital.

The Tourist would have a very limited view of the streets around him (similar to real life’s line of sight), no map, and rely on visual memory to navigate a randomly generated maze of streets. A countdown would indicate the time remaining before the tour bus departs, signifying game end.

Art direction

I dreamed of pixelated Haussmannian apartment buildings, cafés and restaurant storefronts, with tiny waiters and bystanders, inspired by the amazing Pico8 pixelart style of artist Johan Vinet from Tribute Games:

Just imagine a pixelated tourist running around with a camera and no map!

The “gap” between imagination and execution quickly set in when I doodled these preparatory sketches:

Camera? Check! Map? Check! Helpless running around? Check!

This funny premise was definitely too heavy on art, and having slacked on my Pixel Dailies practice I quickly hit a wall. Impossible to get a decent game mockup out of my head, or come up with a style for my tiny tourist (smaller assets are faster to produce during a jam).

I almost pivoted to a different idea: a search and rescue game where the player pilots an helicopter to pick up shipwreck survivors stranded on islands. Cargo load and fuel consumption would impact the helicopter’s range and the player’s ability to return safely to home base, providing some tension to the gameplay yet allowing player decisions to influence the game outcome. The map could have been procedurally generated. But I ended up discarding the concept since aircrafts can fly in straight lines over obstacles, so the map would have been relegated to a mere background rather than an active component of the gameplay.

I started to despair when a wise friend convinced me to power through my creative drought and tackle A Tourist In Paris’s gameplay implementation first by using simple placeholders instead of pixelart. That went against my convictions (see lesson learned #2 in my previous postmortem or how to keep it fun for you to create your game) but was decisive in helping me finish the game in time and gave A Tourist In Paris its abstract style.

Technical choices

For the map of Paris, I wanted to experiment with procedural level generation. My instinct was to design an algorithm assembling together pixelated versions of LEGO City plates, just like I did as a kid to craft many different brick towns.

6-year-old me managed it, so how hard could it be?

Turns out it’s not as simple as it seems to connect all these streets together in a sensible manner while avoiding unreachable “islands” within the map:

Yup, Paris can be sneaky with the Tourist :(

I gave the problem a couple shots, then moved on: Better have a finished game where a couple monuments can’t sometime be visited than have a perfect level generator but no game making use of it.

Overall, I used 36 functions and 72 global variables (about 20 of them are array indices as I tried a new data approach to reduce the code size). Before optimizations, A Tourist In Paris is composed of:
- 188 bytes of HTML markup,
- 578 bytes of charset in PNG format,
- 23,176 bytes of JavaScript game code (including 795 byte of base64 encoded charset)
I used a Gulp pipeline to stream the code into RollupJS (to bundle my ES6 imports into a single file), Babel (to transpile my ES6 code down to ES5), UglifyJS (to minify my JavaScript code) and inline the generated Javascript in the HTML page. After these basic optimizations, A Tourist In Paris was composed of a single HTML file of 8,836 bytes that compressed down to a 4,258 bytes Zip, leaving 68% of the 13kb limit free for more features.

It’s no surprise space wasn’t a concern, given the absence of any pixelart. Since my motivation had ups and downs, I didn’t keep track of how much time I spent making A Tourist In Paris, but I wouldn’t say more than 24h of total development time.

What went right

Looking back at the list of things I said I would do differently after last year’s JS13KGAMES, it seems I’ve actually managed to knock them all off:

  • The game goals are explained on a dedicated screen displayed just before the game starts.
Fun fact: the background color is different everytime.
  • I used a pixelart font (courtesy of Peters and Flo- who created it for their JS13KGAMES 2016 game Glitch Hunter). A 1px black outline helps the text stand out regardless of the background colors, which was useful since mine were unpredictable by design.
  • The score is the number of monuments the player has been able to visit before his tour bus left, and the player can tweet this number when the game ends (many thanks to Ryan Malm who gave me permission to use the Twitter code from his JS13KGAMES 2016 game Super Glitch Box)
The tweet generated by the game for a player to share his score.
  • I finally figured out a collision response algorithm for diagonal motions against a obstacle. It’s far from physically accurate, but it does the trick: I am not trying to find the exact collision point (“rewind” or “backtrack”) and move the player along the wall by the right amount from that point (“slide”). Instead I merely push the player back outside the wall (“pushback”). The frame rate is fast enough that the player doesn’t have time to go very far into the wall. After correction, he ends up pretty close to where he should have been, so it’s still convincing enough.
Accurate response on the left, approximate response on the right.
  • I implemented mobile controls (they’re far from perfect, but it’s a first step). On top of allowing me to enter the less crowded Mobile category of the jam (53 submissions versus 222 for the Desktop category), it was close to my heart that A Tourist In Paris could be played on a smartphone or tablet: I often read about new indie games on my mobile device during my commute, and I’m always frustrated when I can’t get past their title screen because they only support keyboard & mouse. I seldom have time to go back to these games once I’ve arrived at home (or work), and it’s a just wasted opportunity for both the game developers and the potential player.

Lessons learned

#1 — Have your boilerplate code ready before the jam begins

Unlike JS13KGAMES 2016 which was my very first game jam, I‘ve been lucky not to start from zero. I participated in 2 other game jams before JS13KGAMES 2017 and iterated on the top-down genre (a conscious decision not to have to deal with simulating gravity until I solved other topics like collision response), incrementally pushing my game framework further along.

Yet each jam, I lost a bit of time experimenting with the best approach to get started: is it easier to copy the bits of generic boilerplate (game loop, input handling, utilities and build script) from the previous game into a completely new project, or rip out the code specific to the previous game until only a generic framework remains?

Either way, it pays to have answered this question before the jam starts. Time is limited, so better spend it on creating unique features which will set your game apart than on plumbing.

#2 — Have a few game ideas ahead of the jam.

I surely came up with a funny premise but struggled to find unique game mechanics or balance a gameplay relying exclusively on luck.

Code golfer Maxime Euzière (also known as xem) shared a valuable tip on how he approaches this problem in his post-mortem on Lossst,:

Since last JS13k, I’ve listed a dozen game concepts on paper (mostly puzzle or reflection games, because that’s the genre I like the most), and waited for this year’s theme to see which idea would suit it the most…

Maybe the theme will inspire you. Maybe you won’t need such an idea list. But it doesn’t hurt to have a backup plan with several options in your pocket.

#3 — Join the JS13KGAMES Slack channel

js13kgames.slack.com is a great way to get to know the other JS13KGAMES participants, bounce game ideas off each other, share your progress, ask for help on HTML/CSS/JS issues or help others with their problems, ask for feedback on your game demo or playtest others’ demo, make friends and chat with Andrzej Mazur (the super hero who created JS13KGAMES in 2012 and has organized every edition since)… in one word, be a part of the amazing online community you’ve just joined by entering the JS13KGAMES contest. Get your invite at slack.js13kgames.com.

#4 — Know your code obfuscator

Code obfuscators like UglifyJS or Google Closure Compiler are an easy way to compress your game, but how you write code influences the obfuscators' efficiency.

Global variables: By default UglifyJS will aggressively shorten local variable names. It’s safe to do so because they can’t be accessed outside of their function scope. However, it will leave global variable and function names untouched since they could be referenced in other JavaScript files that UglifyJS isn’t processing (as would be the case if you were publishing a library like jQuery).

// src/game.js
var atlas = { hero: new Image('hero.png') };
function render() {
var posX = 0,
posY = 0;
drawImage(atlas.hero, posX, posY);
}
render();~$ uglifyjs src/game.js -o dist/game.js --mangle// dist/game.js (indented for clarity)
var atlas = { hero: new Image('hero.png') };
function render() {
var a = 0, // only local variables get renamed
b = 0;
drawImage(atlas.hero, a, b);
}
render();

For a game, this is an opportunity to save some extra bytes. By wrapping your entire code into an IIFE (immediately-invoked function expression), your globals turn into variables local to the IIFE scope and code obfuscators can do their magic.

// src/game.js
(function() { // IIFE
var atlas = { hero: new Image('hero.png') };
function render() {
var posX = 0,
posY = 0;
drawImage(atlas.hero, posX, posY);
}
render();
})()
~$ uglifyjs src/game.js -o dist/game.js --mangle// dist/game.js (indented for clarity)
(function() {
var a = { hero: new Image('hero.png') };
function b() { // variables local to IIFE gets renamed
var c = 0,
d = 0;
drawImage(a.hero, c, d);
}
b();
})()

The IIFE does cost you a couple of extra bytes but has the benefit of hiding your code inside the function scope, protecting your global variables from being overwritten by outside scripts.

If you’re not concerned by global collisions, you can do away with the IIFE by passing the --toplevel flag to UglifyJS which instructs it to treat globals like locals.

// src/game.js
var atlas = { hero: new Image('hero.png') };
function render() {
var posX = 0,
posY = 0;
drawImage(atlas.hero, posX, posY);
}
render();~$ uglifyjs src/game.js -o dist/game.js --mangle --toplevel// dist/game.js (indented for clarity)
var a = { hero: new Image('hero.png') };
function b() { // renamed even though global
var c = 0,
d = 0;
drawImage(a.hero, c, d);
}
b();

Object properties versus Array indices: UglifyJS won’t rename object properties because of the way they can be accessed. If you only pull your object properties via the dot notation or square brackets with explicit string literals, then the --mangle-prop flag can save you some more bytes. However if you access your property via dynamic variables (which is often my case), this flag will break your code.

// src/game.js
var atlas = { hero: new Image('hero.png') };
var entityType = 'hero';
function render() {
drawImage(atlas.hero, 0, 0); // dot notation
drawImage(atlas['hero'], 10, 10); // square brackets with strings
drawImage(atlas[enemyType], 20, 20); // square brackers with var
}
~$ uglifyjs src/game.js -o dist/game.js --mangle --mangle-props --toplevel// dist/game.js (indented for clarity)
var a = { a: new Image('hero.png') };
var b = 'hero'; // value does not get renamed to 'a'
function c() {
drawImage(a.a, 0, 0); // ok
drawImage(a['a'], 10, 10); // ok
drawImage(a[b], 20, 20); // KABOOM! a.hero no longer exists
}

A workaround is to use arrays in place of objects and access values by index rather than properties.

// src/game.js
var indexHero = 0,
indexEnemy = 1;
var atlas = [ new Image('hero.png'), new Image('enemy.png') ];function render() {
drawImage(atlas[indexHero], 0, 0);
}
~$ uglifyjs src/game.js -o dist/game.js --mangle --toplevel// dist/game.js (indented for clarity)
var a = 0,
b = 1;
var c = [ new Image('hero.png'), new Image('enemy.png') ];function d() {
drawImage(c[a], 0, 0); // index renamed like other variables
}

I’m glad I tried this method in A Tourist In Paris but feel it’s both a blessing and a curse: I did get better compression than by using objects but the code was slower to write, harder to read, and reordering values within the array was error prone, if I accidentally mixed up the indices.

In conclusion (or “so… I made a maze game”)

I am a bit ambivalent about my creation. On the one hand, a funny premise isn’t a substitute for interesting game mechanics or a balanced gameplay. Deprived of its pixel art assets, A Tourist In Paris is left exposed for what it really is: a random maze game where you can’t utilize any of your skills to influence your fate. The gameplay relies on having the good luck of stumbling upon the monuments quickly enough. On the other hand, this sense of helplessness, the unfair nature of random exploration and the implacable bus departure countdown play well with the theme as they capture the essence of being lost.

Even though A Tourist In Paris didn’t place well in the competition, I’m satisfied to have shipped one more game. Accepting early on that a pixelart style was not to be, this time, forced me to make the most of my procedural placeholders and experiment until they developed an abstract style of their own. I also made significant headway on technical issues that had plagued my past games’ development (in particular the collision response), and which will be helpful for future game jams.

If you’ve been looking for an excuse or a motivator to get started on this game idea of yours, JS13KGAMES is a perfect opportunity: it’s a lot of fun to participate in and a great place to learn from others (there are hundreds of tricks to discover by skimming the code of past entries). The 13kb limitation will force first timers to imagine a short game rather than an unreasonably big one, and challenge the seasoned game developers to cram in all the content and levels they dreamed of in their contest submission.

--

--

Jerome Lecomte
Jerome Lecomte

Written by Jerome Lecomte

Software engineer turned manager and father of four, I create pixel art, video games and visual experiments on the Web.

No responses yet