JS1K 2014: Lessons learned

So, what is it about shrinking some code to the point of unreadability that keeps people entering the same competition year after year?

The premise of JS1K is quite simple: fit something cool in 1024 bytes of JavaScript; so, why do I personally enjoy it? Doing a demo, animation or game without constraints is just a matter of time and patience, but when adding a highly strict, almost absurd limitation like fitting everything in 1Kb, it automatically becomes a lot more difficult, and therefore, challenging.

So, there’s a lot of advice online on tips & tricks to optimize code for JS1K (like this blog post), so I won’t repeat it; here’s a few things I learned this year that may be helpful to other people.

Automatic JS code reduction

What I used is Uglify 2 (online version here); make sure to uncheck the “Mangle” option, and check the options under “Compressor…”; other than great minification, it gives some cool options to avoid some automatic processing that may be undesirable, like evaluating constant expressions.

Then I used RegPack 3 (online version here); it’s like JS Crush on steroids, and I want to give kudos to the author for the amount of effort and thought that has gone into it.

It’s nice that it gives both JS CRUSH and RegPack outputs, and also it’s possible to change the “Tiebreaker”, which in my case reduced some bytes by using the non-default “most copies first” option.

8-bit sprite drawing

I stumbled onto a wall trying to efficiently animate some areas of my JS1K entry, and then I remembered a very cool entry from a previous year that actually ended in 8th place.

This is the key source code for the sprite drawing:

T=parseInt

// color palette
P='000fb6853345789593fffff6933ccc444555222f00'.match(/.../g)

// insane sprite drawing method
S=function(z,x,y,w,s){for(i=0,f='';i<z.length;i++)f+=new Array((o=P[z[i].charCodeAt(0)-97]||'',n=T(z[i+1]),(n?(i++,n):1)+1)).join('"'+o+'",');for(z=eval('['+f+']'),i=0,r=0,l=0;i<z.length;(r=(++r)>=w?0:r),i++,l=T(i/w))if((v=z[i]))a.fillStyle='#'+v,a.fillRect(x+(r*s),y+(l*s),s,s)}

Quite insane, if you ask me, but works wonderfully! What it does is to define a very efficient syntax to represent 8-bit graphics, which uses a form of run-length encoding.

So, let’s take a sample string: ab2c3b2a; first, it will “explode” this string, so each letter will be represented as many times as the number next to them (up to 9); hence, the sample string will become “abbcccbba” (when no number is present, “1” is assumed); the resulting data will be rendered sequentally as squares of the size specified by the s parameter, going into a new line each w squares; each letter is a color (‘a’ is the first color in the palette, ‘b’ is the second, etc), and squares will be rendered transparent when using undefined colors (‘x’, for instance). This method is great for representing sprites with low color variety, which was exactly my case.

In the original JS1K entry, this call:

S('j4x3b3j2x2b4jx2j2bj2x2j2e3x2je4x2e2d2e2xe2be3xe6xd7',520-p,230,7,10)

Renders this:

Wizard

Central point

So, quite a lot of the code consists of rendering something at point x,y; the drawing area was 400 pixels wide and tall, so quite a lot of the coordinates (which, unfortunately, didn’t follow any pattern as I was trying to duplicate a hand-made graphic) had 3 digits; one optimization that worked quite well is to try to find the point that was the closest to all the other points.

In my case, that point was 146,150; then, I represented all the coordinates as operations related to that point; for instance, point 151,155 was represented by 5+146,150+5; because it was used many times, RegPack automatically compressed +146,150+ into a single byte, making a coordinate that took 7 bytes into one that takes 3, which is great when the same operation is being used many times over.

Conclusions

In this current age of modern computing, where computing is measured in Terabytes and Gigahertzs, it’s so easy to take the easy way and waste resources like there’s no tomorrow; computing power is relatively cheap compared to good software design and optimization.

So, what is JS1K good for? IMHO, the first and most important lesson to learn is not giving up; there’s always some additional optimization that can be done… sometimes it even requires to go back a few steps and redoing a lot of the work, but that’s the cost of reaching the objective; nobody said it was easy!

Now, from a more practical point of view, I believe that this competition is great for learning the finer points of JS; operator priority, when can brackets be skipped, different ways of achieving the same results…

Then there’s the identification of patterns; one of the most effective ways of reducing code, specially when using JS compressors like JS Crush and RegPack, is to try to identify all similar points and trying to “equalize” them, so they’ll be compressed further; also, reusing the same logic for different functions is a great way to learn abstraction.

… and of course there’s the improved code reading skills that come from working on minified code that relies on global variables :)

comments powered by Disqus