Detective Moji postmortem : a javascript game in just 1024 bytes



It’s some time I don’t write a postmortem, or a post 🙂
But here’s something that could be interesting and a good reference for future projects, too!
This post will be about golfing, javascript golfing and a postmortem about my entry to this year’s JS1024 competition.

As many already says, golfing is pretty fun, because it’s actually solving a puzzle with logic and code tricks knowledge!
It’s practically is experimenting and learning new things while solving a coding puzzle!
And that, I think, makes it very interesting for any coder.
There are great golfer out there and I’m not an expert in the field, having started this extreeme golfing just few months ago and just as hobby, but I hope you can find this post and the techniques I’ll write about useful!
It’s a long read, so feel free to skip any part 🙂

There are different golfing competitions out there.
I’ve started with js13kgames some years ago, and that inspired me to want to explore more of the golfing world. Then I wanted to participate in the annual js1k but this years it didn’t happen! We had a 2kplus jam instead, in which I participated with my 2kPuzzle. It has been hard to add all the features in 2k, maybe one day I’ll write a post about it, too.
Still 2k is not 1k so I still had that question inside me:  I can create a game in 2k, but can I actually create one in 1k?
Finally, in July 2020 some of these great golfers from the community, launched a new annual competition: js1024.
And yeah, in the end I could create a game in 1024 bytes, and here’s how 🙂
But first of all, here’s the actual game!


Click here to play Detective Moji.
Find and click the correct emoji within the time limit!

If you want you can also click here to play the entry directly from the js1024 competition website.


In js1024 there are different categories: canvas, p5js, shader and no shim.
Everyone is free to pick one or more and submit a demo.
Since I make canvas games, canvas and no shim it is for my game.
But nothing too ambitious, my goal is to learn and having fun 🙂

The demo has to be simple, obviously but to be a game it has to have game features.
So I choose to create a simple state machine, with Title, Game and Game Over screens, and to have a score and best score, to add a simple improving challenge.

As for the mechanics, I had in mind some simple action game, but then I wanted something more simple, like a brain teaser or a memory game, like “find the couples”.
But then I choose something even more simple, not a memory but a visual base mechanic, and I opted for a “find the object within the given time” game.

Graphics. Well, not that you can have too much graphics in 1024 bytes. The choice was between colored shapes and letters. But wait. As I learned from Xem’s Lossst (my favourite js13kgames of all time), I can also use Emojis!
So I choose to use Emojis because they really fit a “find the object” idea.

But I wanted to go a little further, adding some character.
A game, to be a better game, needs to create a feeling of identification within the player.
This is usually obtained by adding context to the mechanics, a setting, a background story, and some characters to empathize with.
But how to do all of this with just 1024 bytes?
I added a detective emoji in the main screen.
Simple and effective.
This detective emoji not only adds an actual character to the game, to empathize with (who is he? what is he doing? oh it’s a detective!), but also, it adds an unwritten story (oh it’s a detective case, etc) and so a setting to it.
And, all of this, actually gives meaning, a goal, to the “finding” mechanics (Is that an identikit?), making the game evolve from just a visual challenge “find the correct emoji” to a more gamish “hey detective! Make the correct identikit, picking the right face!”.

Finally, I called this game “Detective Moji” (which is actually cheating because the game name is not in the code), reinforcing the idea of being playing a detective game, and also giving the main character a first name, which, easily creates more identification.
Yeah, I golfed game design here, too 🙂


The code structure is as follows:

  • var declaration
  • update function
  • click listener
  • game over function
    cannot be anonymous because it needs to be called from both update and listener
  • get random object function
    cannot be anonymous because it needs to be called for center element and objects
  • generate emoji function (restart level)
    cannot be anonymous because it needs to be called from itself and click


This is not the first draft of the code, because I didn’t want to create a postmortem at first 🙂
But still it is a commented readable version, already showing some basic golfing techniques, and without the other golfing techniques that can make the code unreadable!

 Remember, from the shim we have:
 a, // canvas
 b, // body
 c, // 2D canvas context

MAX_TIME = 9 //maximum time, 9 instead of 10 to save 1 char :)
CURRENT_STATE = BEST_SCORE = 0 //chain setter example
w = a.width //game width
h = a.height //game height
FONTSIZE = w * .045 //setting font size, based on canvas width
c.font = FONTSIZE / 13 + 'em"' //setting font for canvas, using em instead of px to save space
color = '#000' //color, needed to change fillstyle
emojiArray = '😂0😲0😬0😮0😰0😟0😎0😆0😐0😑0🙄0😏0😗0😙0😚'.split(0) //emoji list, using split instead of plain array
objectsArray = [] //on screen objects array, containing all emoji objects. '40' around the code is p.length hardcoded!
centerElement = {} //central element, the object to pick
centerElement.x = X = w / 2 - FONTSIZE //setting values of a new var X because I use centerElement.x call more multiple times
centerElement.y = Y = h / 6 //same for Y
RANDOM = Math.random //setting random since it is used a lot
//we can set SCORE, TIME, MAX TIME and TIME_COUNTER later, since they are always set before checked on.

setInterval(x => {
    //fill bg gray and back to black fill
    c.fillStyle = '#ccc'
    c.fillRect(0, 0, w, h)
    c.fillStyle = color

    //STATE 0
    //draw play emojy
    if (CURRENT_STATE == 0) {
        c.fillText('▶', w / 2 - FONTSIZE * .5, h / 2)
        c.fillText('🕵️', w / 2 - FONTSIZE * .5, h / 2 - FONTSIZE * 2)
    //STATE 1
    else if (CURRENT_STATE == 1) {
        //draw every emoji object, rotating them, individually
        for (i = 40; i--;) {
            currentObject = objectsArray[i]
            c.translate(currentObject.x, currentObject.y) //translate
            c.rotate(currentObject.rotation += currentObject.rotationSpeed) //rotate
            c.fillText(currentObject.graphic, -FONTSIZE * .8, FONTSIZE / 2.1) //draw, with manual adjustments based on font size to simulate center alignment
            c.restore() //restore

        //draw emoji to pick and score texts
        c.fillText(centerElement.graphic, X, Y)
        c.fillText(SCORE, X - FONTSIZE * 3, Y)
        c.fillText(TIME, X + FONTSIZE * 3.9, Y)

        //decrease time based on a counter
        if (TIME_COUNTER == 30) {
            TIME_COUNTER = 0
        //check time over
        if (TIME <= 0) GAMEOVER()

    } else {
        //STATE 2
        //draw emoji and scores
        c.fillText('🏁', X, Y)
        c.fillText('⭐ ' + SCORE, X, h / 3)
        c.fillText('👑 ' + BEST_SCORE, X, h / 2)
}, 30)

a.addEventListener("click", e => {
    //reverse logic and use of ternary operators instead of if to save chars
    //if state not 2
    2 != CURRENT_STATE ? (
        //if state is 0
        1 != CURRENT_STATE ? (
            CURRENT_STATE++, //set state 1 (if not 2 and not 1 so its is 0 and CURRENT_STATE++ is 1)
            SCORE = TIME_COUNTER = 0, //initialize/reset score and time counter
            TIME = MAX_TIME, //set/initialize time to max time
            generateEmojis() //generate emojis
        ) :
        //if state is 1
        //if find and emoji near click and with correct graphics
        //using math.hypot to chekc distance between points
        objectsArray.find(x => Math.hypot(x.x - e.x, x.y - e.y) < FONTSIZE / 1.5 && x.graphic == centerElement.graphic) ? (
            SCORE++, //increase score
            TIME = TIME < 2 ? 2 : MAX_TIME - SCORE, //update time based on current score (minimum 2)
            TIME_COUNTER = 0, //update time counter
            generateEmojis() //generate emojis
        ) :
        //else game over (so even if the click is outside of emojis)
    ) : CURRENT_STATE = 0 //if state 2 go to state 0

function GAMEOVER() {
    CURRENT_STATE = 2 //set state

function getRandomObject() {
    // "0 |" used instead of Math.floor, since it's a positive number
    return emojiArray[0 | RANDOM() * 15]

function generateEmojis() {
    for (i = 40; i--;)
        objectsArray[i] = {
            graphic: getRandomObject(), //graphic
            rotation: 0, //rotation
            rotationSpeed: RANDOM() / 5 * (RANDOM() * 2 | 0 || -1), //rotation speed
            x: w / 5 + FONTSIZE + i % 8 * FONTSIZE * 1.5, //position
            y: h / 3 + (0 | i / 8) * FONTSIZE * 1.5 //position

    //random graphic for central object (emoji to pick)
    centerElement.graphic = getRandomObject()

    //check to see if there is at least one correct object to click
    //literally find an object with same graphics as centerElement, or generate everything again
    objectsArray.find(x => x.graphic == centerElement.graphic) || generateEmojis()


In this first version, you can already see these basic tricks below.

hardcoding length and length calculations



chain setters
B = A = C = T = 0

value increment

++ instead of +=1 to increment

true and false

!0 instead of true and !1 instead of false

Math.floor for positive numbers

Use ~~ and 0| instead of Math.floor for positive numbers

Ternary operators with functions
if (a == b) {
} else {
    if (a == d) {

can be replaced with


Generic byte saving optimizations






R() < 0.5 ? -1 : 1


R() * 2 | 0 || -1

object setters
e = {}
e.x = 0
e.y = 1
e.z = 2


e = {x:0,y:1,z:2}

saving 1 char, removing “e” you need commas but not on the last element!

using find instead of for
function atLeastOneCorrectEmoji() {
    for (i = 40; i--;) {
        if (objectsArray[i].graphic == centerElement.graphic) return !0
    return !1

if !atLeastOneCorrectEmoji() generateEmojis()


(even using || instead of if)

objectsArray.find(x => x.graphic == ì.graphic) || generateEmojis()

reversed logic to check states in click listener

(this project specific logic optimizations)
Before, it double checked if you clicked an emote and if it was the right one, and if it was wrong it was Game Over. Optimizing it, now it goes Game Over even if you click an empty space, saving a lot of space.
All of this checked in reverse, to use ternary operator avoiding ifs.

distance between points using hypot invece di sqrt
Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y))



removing if brackets

using commas or keeping it single operation

using arrow functions and anonymous functions

whenever possible

reduced tmax from 10 to 9

to save 1 char

removed some emojis, picking better emojis

with some criteria like color, shape etc to make the game more enojoyable

using em instead of px
c.font = FONTSIZE / 13 + 'em"'

emojiArray = '😂0😲0😬0😮0😰0😟0😎0😆0😐0😑0🙄0😏0😗0😙0😚'.split(0)

instead of

emojiArray = ["😂","😲","😬","😮","😰","😟","😎","😆","😐","😑","🙄","😏","😗","😙","😚"]

reversed for loops

from last element to first, end condition is silently set
to value <=0
that saves space!

You can see these tricks already implemented in the readable code.
Some of them are easy to understand, well known and already used in daily life.
But some of them were new to me, I learned them during the competition, checking other people codes, articles, guides etc.
Check the links below, to see all references!


Now, some very interesting tricks.
To save even more space, there are more powerful techniques, which I didn’t put on the above code, since they make everything pretty much unreadable.
But they are great, check them out:

single letter vars

simple, powerful, as you can imagine, to save chars, but yeah it’s the one most responsible for the code becoming unreadable 🙂

assign in function calls + combine nested checks

I’ve taken this directly from here.
Practically you assign values in calls and combine the nested checks.
For example in the update function we have a counter B which will decrease the time T, once every 30 steps, becoming 0 again. Then, T is checked for Game Over (time over).

if (B == 30) {
    B = 0
if (T & lt; = 0) G()

checking only when it changes, assigning inside the check and using operator to mod

if (B++%30==0) if (T-- <= 0) G()

becoming, with nested if and && operators
(where <= is simply <)

if (B++ % 30 == 0 && T-- < 1) G()

can you understand me now when I say code golfing is like solving a puzzle? 🙂

canvas context hash trick

Check this one I learned around, but firstly read here.
We can use this (where c is canvas context):

for(Z in c)c[Z[0]+(Z[6]||Z[2])]=c[Z];

To duplicate with a shorter name all the canvas functions so that, for example to call c.fillRect() we can just call c.fc()
This is really smart! It saves a lot of space if we have a canvas project!
Well, this really seems like puzzle, or maybe magic, right? 🙂

shorten function with Function

This one is one of my favourites, too, and I’ve taken it from here
If we set the Function declaration to a variable, like this:
P = Function
We can then transform all the functions like this:

Game Over function:

function GameOver() {
    L = S & gt;
    L ? S : L
    U = 2


GameOver = P('L=S>L?S:L;U=2')

function getRandomObject() {
    return ù[0 | R() * 15]


getRandomObject = P('return ù[0|R()*15]')

Same for getRandomEmoji() function
Saving a lot of space!

And, including these techniques, the whole code becomes:


Remember, from the shim we have:
a, // canvas
b, // body
c, // 2D canvas context

for (Z in
    t = 9, //MAX TIME
    w = a.width, //GAME WIDTH
    h = a.height, //GAME HEIGHT
    F = w * .045, //FONT SIZE
    c.font = F / 13 + 'em"', //setting font for canvas
    ò = '#000', //color
    ù = '😂0😲0😬0😮0😰0😟0😎0😆0😐0😑0🙄0😏0😗0😙0😚'.split(0), //emoji array
    p = [], //objects array, containing all emoji objects. '40' around the code is p.length hardcoded!
    ì = {}, //central element, the object to pick
    ì.x = X = w / 2 - F, //setting values
    ì.y = Y = h / 6, //setting values
    R = Math.random, //setting random since it is used a lot
    P = Function, //setting Function declaration

    c) c[Z[0] + (Z[6] || Z[2])] = c[Z];
//for(Z in c)c[Z[0]+(Z[6]||Z[2])]=c[Z];
//is a little great hack
//inside of which we can also put all the setters

setInterval(x = & gt; {
    //fill bg gray and back to black fill
    c.fillStyle = '#ccc'
    c.fc(0, 0, w, h)
    c.fillStyle = ò

    //STATE 0
    if (U == 0) {
        c.fx('▶', w / 2 - F * .5, h / 2)
        c.fx('🕵️', w / 2 - F * .5, h / 2 - F * 2)
    //STATE 1
    else if (U == 1) {
        //draw every emoji object
        for (i = 40; i--;) {
        à = p[i] //save
        c.ta(à.x, à.y) //translate
        c.rt(à.r += à.s) //rotate
        c.fx(à.g, -F * .8, F / 2.1) //draw, with manual adjustments based on font size to simulate center alignment //restore
        p.find(à = & gt; {
  , //save
                c.ta(à.x, à.y), //translate
                c.rt(à.r += à.s), //rotate
                c.fx(à.g, -F * .8, F / 2.1), //draw, with manual adjustments based on font size to simulate center alignment
        }) //restore)

        //draw emoji to pick and score texts
        c.fx(ì.g, X, Y)
        c.fx(S, X - F * 3, Y)
        c.fx(T, X + F * 3.9, Y)

        //decrease seconds based on a counter
        //and go to game over if time is finished

        thanks to
        assigns in function calls + combine nested checks

        if (B == 30) {
        B = 0
        if (T <= 0) G()


        if (B++%30==0) if (T-- <= 0) G()

        which then becomes

        if (B++ % 30 == 0 & amp; & amp; T-- & lt; 1) G()

    } else {
        //STATE 2
        //draw emoji and scores
        c.fx('🏁', X, Y)
        c.fx('⭐ ' + S, X, h / 3)
        c.fx('👑 ' + L, X, h / 2)
}, 30)

a.addEventListener("click", e = & gt; {
    //if state not 2
    2 != U ? (
        //if state is 0
        1 != U ? (
            U++, //set state 1 (if not 2 and not 1 so its is 0 and U++ is 1)
            S = B = 0, //initialize/reset score and time counter
            T = t, //set time to max time
            j() //generate emojis
        ) :
        //if state is 1
        //if find and emoji near click and with correct graphics
        p.find(x = & gt; Math.hypot(x.x - e.x, x.y - e.y) & lt; F / 1.5 & amp; & amp; x.g == ì.g) ? (
            S++, //increase score
            T = T & lt; 2 ? 2 : t - S, //update time based on current score (minimum 2)
            B = 0, //update time counter
            j() //generate emojis
        ) :
        G() //else game over (even if click outside of emojis)
    ) : U = 0 //if state 2 go to state 0


since we set P as Function declaration
the Game Over function, from

function G() {
L = S > L ? S : L //L = Math.max(S, L) SET HIGH SCORE

G = P('L=S>L?S:L;U=2')


same as Game Over function
O = P('return ù[0|R()*15]') //'15' is ù.length hardcoded

j = P('for(i=40;i--;)p[i]={g:O(),r:0,s:R()/5*(2*R()|0||-1),x:w/5+F+i%8*F*1.5,y:h/3+(0|i/8)*F*1.5};ì.g=O(),p.find(c=>c.g==ì.g)||j()')

It is still commented and, if you followed the logic, readable enough!


To remove all the comments and further minifying it, I’ve used a web javascript minifier and the code became like this:

for(Z in t=9,U=L=0,w=a.width,h=a.height,F=.045*w,c.font=F/13+'em"',ò="#000",ù="😂0😲0😬0😮0😰0😟0😎0😆0😐0😑0🙄0😏0😗0😙0😚".split(0),p=[],ì={},ì.x=X=w/2-F,ì.y=Y=h/6,R=Math.random,P=Function,c)c[Z[0]+(Z[6]||Z[2])]=c[Z];setInterval(t=>{c.fillStyle="#ccc",c.fc(0,0,w,h),c.fillStyle=ò,0==U?(c.fx("▶",w/2-.5*F,h/2),c.fx("🕵️",w/2-.5*F,h/2-2*F)):1==U?(p.find(t=>{,c.ta(t.x,t.y),c.rt(t.r+=t.s),c.fx(t.g,.8*-F,F/2.1),}),c.fx(ì.g,X,Y),c.fx(S,X-3*F,Y),c.fx(T,X+3.9*F,Y),B++%30==0&&T--<1&&G()):(c.fx("🏁",X,Y),c.fx("⭐ "+S,X,h/3),c.fx("👑 "+L,X,h/2))},30),a.addEventListener("click",c=>{2!=U?1!=U?(U++,S=B=0,T=t,j()):p.find(t=>Math.hypot(t.x-c.x,t.y-c.y)L?S:L;U=2"),O=P("return ù[0|R()*15]"),j=P("for(i=40;i--;)p[i]={g:O(),r:0,s:R()/5*(2*R()|0||-1),x:w/5+F+i%8*F*1.5,y:h/3+(0|i/8)*F*1.5};ì.g=O(),p.find(c=>c.g==ì.g)||j()")

If you want you can beautify it back, to make it more readable.

I used this javascript minifier a lot. Yeah I could have used an automated uglyfier process like Google Closure Compiler but the goal here is to learn 🙂

Often I minified and beautified back the code to see which optimization it did, learning it and using it myself manually. This looks useless but actually made me discover new tricks, learn new javascript things I didn’t know and also discover different logics I could have used.
Oh, and also it’s useful to better find out which part of the code needs to be golfed more!
Like “too many ifs?”, “too many canvas calls?” etc.

The minifier itself isn’t enough though. Yes it optimizes loops, calls, variables and decalarations, but it cannot optimize the logic, hardcode things, declare P as Function, use hypot instead of sqrt, assign or simplify Math calls, strings instead of arrays etc.
It is powerful, but it only does a small percentage of all the golfing, that’s because, in most of the cases the optimizations are really specific.
My day one code just went from 4kb to 3kb after the automatic minimization!
While it became 939 bytes in the end!


Can all of this be adapted outside the shim in just pure html?
Well we’ll have to re-create a mini shim like this

<canvas id=a><script></script>
c = a.getContext("2d")
a.width = innerWidth
a.height = innerHeight

obviously we’ll put these these 3 setters inside the initial loop
and also we can set innerWidth and innerHeight while setting w and h, too

w = a.width = innerWidth
h = a.height = innerHeight

and voilà, this is the actual final version of Detective Moji’s source code:

<canvas id=a><script>for(Z in c=a.getContext("2d"),w=a.width=innerWidth,h=a.height=innerHeight,t=9,U=L=0,F=.045*w,c.font=F/13+'em"',ò="#000",ù="😂0😲0😬0😮0😰0😟0😎0😆0😐0😑0🙄0😏0😗0😙0😚".split(0),p=[],ì={},ì.x=X=w/2-F,ì.y=Y=h/6,R=Math.random,P=Function,c)c[Z[0]+(Z[6]||Z[2])]=c[Z];setInterval(t=>{c.fillStyle="#ccc",c.fc(0,0,w,h),c.fillStyle=ò,0==U?(c.fx("▶",w/2-.5*F,h/2),c.fx("🕵️",w/2-.5*F,h/2-2*F)):1==U?(p.find(t=>{,c.ta(t.x,t.y),c.rt(t.r+=t.s),c.fx(t.g,.8*-F,F/2.1),}),c.fx(ì.g,X,Y),c.fx(S,X-3*F,Y),c.fx(T,X+3.9*F,Y),B++%30==0&&T--<1&&G()):(c.fx("🏁",X,Y),c.fx("⭐ "+S,X,h/3),c.fx("👑 "+L,X,h/2))},30),a.addEventListener("click",c=>{2!=U?1!=U?(U++,S=B=0,T=t,j()):p.find(t=>Math.hypot(t.x-c.x,t.y-c.y)L?S:L;U=2"),O=P("return ù[0|R()*15]"),j=P("for(i=40;i--;)p[i]={g:O(),r:0,s:R()/5*(2*R()|0||-1),x:w/5+F+i%8*F*1.5,y:h/3+(0|i/8)*F*1.5};ì.g=O(),p.find(c=>c.g==ì.g)||j()")</script>

it works as a standalone html in just 1013 bytes
in some cases you may need to specify utf8 charset for this to work (due to single letter vars and emojis), also doable from code like this:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

But I’m not doing it in the code directly since it’s not needed in this case, more info here.


Being very simple, everything should work fine on Windows and Linux (any browser) and even on Android devices.
I haven’t tested all the possible combinations of OS and browsers, though.

By the way it has compatibility issues on Mac (any browser), which we may call “known bug” maybe.
If you are on Mac, in fact, you can see that Emojis are glitching while rotating.
This is actually a known Mac OS bug, as you can read here (thx kipkat) and try on a dedicated fiddle here.

It appears this bug has no fixes or workarounds, so even if I could easily avoid it by just not making Emojis rotate, I decided to keep it there, as a … bug reference? Case study?
Well whatever, with Emoji rotation the game is better and if you are on Mac, yeah, the Emojis will glitch, but it will also make the game even more challenging 😛


Here are all the references I used to study for this golfing competition, picking the ones I needed as is or re-adapted for my purposes 🙂


My code went from original 1.85 minified to 1087 pretty fast.
Then with all the crazy tricks and logic optimizations it became 939 and 1013 without shim.
This is really awesome!
In the end I can really create a game in just 1Kib!

As you can see I’ve done a lot of researches, which enlightened me, making me want to learn more and more about golfing. For sure I’ll re-use these techniques for future projects, even non golfing ones!
And, as long as it is, I hope this postmortem can be useful to other developers, too 🙂

By the way, probably my code can be optimized more. As you can see there are a lot of CRAZY golfers out there, with even smarter tricks!
So I highly suggest to check all the entries to the js1024 as well as other archives like dwitter, js13kgames and js1k!


Thanks section!
Thanks for all the staff behind js1024, for this new annual competition!
Thanks to the community for continuously sharing demos, guides and art about golfing!
Thanks to xem and FrankForce , which I followed a lot this year, and inspired me to start golfing a bit!
And obviously, thanks to you for reading!


Leave a Reply

%d bloggers like this: