Stand By Me: a JS13KGAMES 2022 post-mortem

Jerome Lecomte
11 min readOct 16, 2022

--

This year was my 7th consecutive year participating in this month-long competition to craft a game with web technologies within a 13kb ZIP limit. I now earned enough JS13KGAMES t-shirts to last me a whole week ;p

The theme was Death, and I made an emotional 2D top-down arcade shooter which placed 27th in the Desktop category. It could be the lovechild between Serious Sam and Alone With You: the Central Core of your ship/station is under attack from parasites. It dispatched a chatty Defense Module Companion to assist you in repelling the infestation. The DMC has your back, but at a cost it wasn’t aware of. How will that affect your relationship amidst the mayhem?

Try it on JS13KGAMES or on my personal website, and check out the postmortems of my previous entries Blade Gunner (2016), A Tourist In Paris (2017), SUBmersible WARship 2063 (2018), Don’t Look Back! VR (2019), Highway 404 (2020) and 2021: a Space Opera (2021)

Game ideation

I found Death to be a tricky theme to stand out: it’s very common as a central component of action games (e.g. kill or be killed), and difficult for me to reverse-engineer its creative uses (e.g. rewind previous life).

Early inspirations included:

  • A quote from the movie Oblivion: “everybody dies, the thing is to die well” and one from Lays of Ancient Rome: “how can man die better than facing fearful odds, for the ashes of his fathers and the temple of his Gods" (I ended up including that last one on the “game over” screen, but with a gender neutral wording so it would be more inclusive)
  • The endless waves of enemies rushing the player in the video game Serious Sam (fitting well with the previous quote)
  • The Empath, an episode of the original Star Trek series where a selfless alien named Gem healed Captain Kirk and Dr McCoy by transferring their injuries to her.
Serious Sam’s endless foes, and Gem the healing empath

I also wanted death to be significant and meaningful, so it would change the way you’d play the game.

The idea of an AI companion unknowingly protecting you at their own expense from hordes of enemies progressively took shape, and here was the moral and emotional dilemma I intended to put the player through: Once you discovered the impact on your companion’s health, would you take advantage of being essentially invincible by putting yourself at risk and kill them in the process, or would you be extra careful to spare their life for as long as possible?

I named the AI Defense Module Companion (DMC for short) to clue in its role in the game while keeping the surprise about how it would perform the defense part.

I named the game Stand By Me, as a nod to Ben E. King’s eponym song, and as a hint to the complicité and growing sense of loss that I hope would develop between the player and DMC.

Art direction

I turned one more time to pixelart for the game’s graphics.

Since picking a color palette is always a struggle, I intended to pull a page of Frank Miller’s Sin City movie and draw all the sprites in black and white only, with vibrant red for the streaks of blood.

1bit palette, plus vibrant colors for highlights

That even triggered some scope creep ideas:

But, on a chance encounter with the following tweet, I fell in love with this 4 color palette: where a black and white palette would result in flat sprites, these beige, cyan and navy would allow to add shading and depth to the sprites, with orange as the highlight color.

The exceptions breaking this convention were the laser bolts and blast radius. They are still the purple and pure cyan colors I used in early development. I left them because the bolts and blast stood out more than if I had used the correct colors from the palette, and I didn’t have the time to address that properly.

I opted to use a thick 1 pixel outline, central to Christina-Antoinette Neofotistou’s (aka Castpixel) whimsical style.

I experimented a bit before landing on a sprite size. My first attempt fit the “big blob” enemy in 16x16px. However, I imagined the smaller scout enemies to look like Zerglings in StarCraft, and there was no way I would managed to draw them in a smaller size. Instead, I supersized the big blobs to 32x32px, and the scouts turned into simpler 16x16px slugs to keep the animations to 3 frames per enemy type.

big blob and slug enemies

For the player, I recycled a 8x8px ninja animation I did for an unfinished project, doubled its size and upgraded their sword to a gun. I even spent time on scope creek that probably not many people noticed: the hero leans forward when moving in the same direction as they’re shooting, and leans backward with their legs animation reversed when retreating from the direction they’re shooting at, as demonstrated below.

tiny ninja and Stand By Me hero

To convey your AI companion’s emotions through the game , I drew inspiration from the expressiveness of Eve (from Disney’s Wall-e) and Gerty (the emoji robot from Moon)

the DMC’s facial expressions were modelled after Eve and Gerty’s

I landed on 11 facial expressions, one for each of the DMC’s health levels.

I represented the enemies’ ingress points as hatches, and the Central Core as a globulous eye thingy.

Technical direction

My starting point was a small, extendable boilerplate for 2D canvas games which I keep improving every year with lessons learned during each JS13KGAMES competition.

On top of this, I’ve reused logic from some of my past JS13KGAMES entries:

  • Inkspace to rotate the gun so it follows the mouse,
  • Blade Gunner to flip my right-facing sprites horizontally at runtime so I could save space by not including the left-facing ones in my spritesheet,
  • Mirror Dimension to manage damage, hit points, temporary invincibility after getting hit, etc.

I’ve also completely simplified the logic keeping track of coordinates. My boilerplate wasn’t very consistent, making some operations in camera space and others in world space, leading to complicated logic that I even had trouble to follow later. So I’ve switched every thing to be only in world space, including the camera position and rendering on the backbuffer canvas. Then I copied only the area covered by the camera rectangle onto the visible game canvas. The mouse coordinates got converted from screen space to camera space, then to world space by adding the camera position to each coordinate.

I didn’t use traditional for() loop anymore, favoring a functional approach with array.forEach(). The drawback was that forEach() will iterate over all array items no matter what. When I knew I’d need to exit the loop early, I used array.find() and ignored the return value, which is a bit counterintuitive but did the trick.

I also typically had a lot of counters to keep track off (e.g. when to switch to the next frame of an animation, when to fire the next bullet based on the fire cadence of a weapon, when to stop rendering a dialog…). I’ve experimented with several approaches during the years, but haven’t standardized on one yet:

  • end time approach: when an event occurs, I store the time at which it should repeat/end by adding its duration to the current game time. The benefit of this approach is that it’s very low maintenance: I calculate this value once, then on every frame I just check if the current game time got past that date. The drawback is that if I slow down or stop the rendering loop for a special effect, that date isn’t correct anymore and the next event will happen too soon.
  • timer approach: on every frame, I add the time elapsed since the last frame to the timer, then check if the timer is above the event duration. If it is, I subtract the duration to the timer and trigger the event. The drawback of this approach is that every timer needs to be updated on every frame. The benefit is that it gives me a chance to reduce or skip the elapsed time when the rendering loop is slowed down or stop, so the timing of the next event is correct. The timer can start at 0 and count up towards the event duration threshold, or start at the event duration and count down towards 0. Both approaches are equivalent, but I haven’t made up my mind on which makes more sense to me.

Lessons learned

1. Team up with a musician

2021 JS13KGAMES grand winner Ryan Malm threw up the towel halfway through the competition this year, but he graciously offered to compose tracks for other contestants:

I messaged Ryan 2 moody tracks from Quake and Quake 2, and the vaguest brief ever: “I think something low in tones, but medium-high in beat/rpm to get a somewhat oppressive feeling”. He created an awesome tune that was praised in practically all the comments left on my game during the voting period. Listen to the track in Soundbox’s online editor.

2. Make great decisions quickly

Under time constraints, I couldn’t implement all the ideas I had in my head, but I was able to cut scope quickly without compromising on the gameplay. For example:

  • Despite the background story asking you to defend the Central Core, enemies never attack it. They attack you instead. In early development, I made enemies hone in on the player to quickly test the combat mechanics. But I realized that if I focused their attention on the Central Core, it’d be easy for you to pick them out from the sidelines. That meant I would have to send some after you, and some after the Core. And keep track of the Core’s health status, and display that information somewhere. And distract you from the real focus of the game, which was your budding relationship with the DMC.
  • Once I had implemented one weapon and 2 types of enemies, I was tempted to add more variety. But “more” doesn’t necessarily translate to “better”. A single, well tuned weapon is more enjoyable than a dozen poorly thought ones. Instead, I invested the time into carefully crafted systems to convey the DMC’s feelings.
  • I initially positioned the DMC face and text zone at the bottom of the screen, but quickly realized that new dialogs weren’t very noticeable due to the way eyes naturally scan screens in Western cultures (top-left to top-right to bottom-left to bottom-right). I moved them at the top and immediately got better results.

3. Keep a developer journal on Twitter

I’ve always been fascinated by the evolution of other contestants’ work-in-progress, from their early prototypes to polished games, so I’ve made a point to share my own progress on Twitter.

This daily commitment 1) kept me motivated, 2) forced me to complete something to show for and brag about, and 3) served as my developer journal to look back and reflect upon after the competition is over. I’ve included the size of each build in every tweet so I could see the evolution over time.

It was also an occasion to keep it light and make bad puns about my game:

Things that didn’t make the cut

Given there is no way to win at my game (I just scripted enough waves of enemies to overtake the player 😱), it would have been nice to have a score to brag about. Ideally, I would have kept track of how long you survived or how many enemies you’ve decimated, and included those in the tweet you can share from the game over screen.

Speaking of the game over tweet feature, its initial implementation was to open a browser window on Twitter’s tweet intent URL with a preset message. I’ve since leveraged the Web Share API which, on mobile, will bring up the native share view containing all the installed apps you can share to: not only Twitter, but also TikTok, Instagram, Whatsapp, Slack…The Web Share API also supports file attachment, and I thought it would be cool to capture a picture of the canvas, so you could share the swarm of enemies responsible for your untimely demise.

JS13KGAMES 2022 Personal favorites (in no particular order)

Dead Again is a top down pixelart survival game, and direct competitor of Stand By Me. Where I focused on a relationship amidst mayhem, Dead Again focused on pure, polished action and weapon/ammo management!

Bardo is another top down vector graphics survival game competing with Stand By Me. Bardo focuses on a paced zen-like ambience of just-in-time escapes. Very satisfying!

Incident at BIO13K is a creative horror game retracing the events of a research lab incident through the viewpoint of a security camera video tape.

EVAC3D is a simple yet impressive night time helicopter simulation written in vanilla Javascript.

Swimming With Sharks is a “who’s dunnit?” point’n’click game with good vector graphics and great humour.

I’ve Been Ghosting is another murder mystery game where you solve… your own death!

The Last Tear is an emotional, positive adventure game about death and loss, which reminded me of the story telling of Home.

Norman the Necromancer is an interesting pixelart game with a great explanation as to why the current session starts where the previous one ended!

13 is an impressive top-down arena server game, with satisfying blood splats and weapon effects.

Dying Dreams is a game where the goal is to die, and use each of your characters’ death to progress through the level. The pixelart is cute and everything is animated!

Soul Jumper is a smart pixelart platformer where you can rewind your last death and use it as a stepping stone to move further.

The Neatness is an ingenious connect-the-dots puzzle game.

Infernal Thrones is an impressive Metroidvania with superb vector graphics.

--

--

Jerome Lecomte

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