Román Cortés

The Entry I Didn’t Submit To Js1k

1 de Abril del 2012

If you are reading this post, you probably know about the love themed js1k competition. I tried to submit a new entry before the deadline on March 14th, but I failed to do it.

I was able to complete it on time, but I noticed a couple of problems during the last minute, so I told Kuvos, organizer of js1k to cancel its release.

The first and most notable problem was speed. I developed the thing on my recently bought high-end PC, and I had the error of not testing it on any older machine during development. In the new PC everything was fast and full framerate, but then I tested in my other machines, I noticed it was overkill, unacceptably slow and totally killing the visual effect.

So I’m publishing now 2 versions of the effect:

Clouds Low Res (recommended)

Clouds High Res (original version, only for high-end PCs)

Be warned that even the low res version might be too much for some old computers and for some browsers. And note it is a bit over 1kb, but it fits in less than 1kb after using JSCrush by aivopaas.

Ok, now I’m going to try to describe a bit what I did:

Making a texture

Perlin noise is specially well suited for clouds, as the own Ken Perlin explains in this presentation (a totally recommended read). Just to summarize how to go from Perlin noise to something resembling clouds:

The only problem with Perlin noise for js1k entries is that a perlin noise implementation is a bit too big for 1kb. It fits in 1kb, but it would not leave too much room for other code.

So I tried to make a texture similar to Perlin noise but by other method: a random walk. In the Wikipedia article about it you will find nice animations showing how it works. The only difference in my implementation, is that I don’t fill a full pixel at each position the walk traverses, but I just a bit, just like if I were using a non-full opacity brush, and this way it ends pretty similar to Perlin.

Random walk implementation details

The random walk needs a PRNG (pseudorandom number generator). As I wanted to create a cloud scene being always the same, I needed a PRNG with the ability to set its seed, and this way creating always on each run the same serie of (pseudo) random numbers.

Now a little problem was that Javascript’s Math.random doesn’t have the possibility to set the seed (not to mention each Math.random implementation would probably produce different results even with the same seed). So, I needed to implement my own PRNG, and to maintain it as little as possible… in js1k each byte counts.

This was my first implementation of the random walk with its own PRNG in 74 bytes:

m=Math.cos;for(x=y=n=9e4;n--;y+=m(x+=m(y*x)))a.fillRect(x%128,y%128,1,.1);

The result was this texture:

The problem with this implementation was that it was relying in Math.cos, and Math.cos doesn’t produce the same results across different browsers, as shown in the Ecmascript spec:

15.8.2.7 cos (x)

Returns an implementation-dependent approximation to the cosine of x.

So, the texture works the same on each run in the same browser, but in some browsers you get different results… so, next try:

for(x=y=l=i=9e4;i--;)l=l*i+i>>>1,x+=l%3-1,y+=1-(l>>4)%3,a.fillRect(x%128,y%128,1,.1);

It takes 11 bytes more up to 85, and we would not have set Math.cos in a single letter variable for posterior use as in the previous example, so in the end it is like occuping around 20 bytes more (a lot in js1k size terms). But it always renders this exact same result across all browsers:

Please note that the PRNG I created here is of extremely low quality, so while it is valid for my purpose it is not recommended for use in anything else. Search for high quality Javascript implementations of PRNGs with configurable seed - like Mersenne twister - if you need one for your own projects.

The Cloud Brush

Here I did the same as in my Christmas Tree from a previous js1k competition. First I create a cloud brush using the previously created texture:

Painting the brush multiple times:

Drawing the clouds scene

I use the noise texture again to decide where to paint a cloud brush. If the texture at (x, y) is above a threshold level, I paint the brush at a position determined by x and y.

I consider y determine how far are the clouds. Further brushes are painted first so nearer are painted on top. To add a perspective sensation, further clouds are painted littler than nearer ones.

Finally, since I’m using only one brush, if it is repeated along a totally straight line it would be very noticeable the repetition causing visible linear patterns. What I do is to use again the noise texture to offset a bit the final positionto paint the brush, and this way I avoid those visual artifacts.

Parallax displacement

Drawing the clouds scene takes almost all the time in the precomputation process, several seconds in the high resolution version even in a high end computer, so it is not possible to render it realtime with canvas right now.

What I did was to pre-render it into several layers (multiple canvases). I used 22 in the high res version, 11 in the low res version. Each layer contains a big amount of brushes painted on it, grouped by the distance to the camera:

Moving the layers at different speeds, as I explained it in the CSS 3D Meninas post, cause the parallax 3d effect.

Heart-shaped rainbow

Could I have been more kitsch? Not sure, but it fits the love theme of the competition.

What I did was to draw multiple bezier curves, all with very few opacity and some displacement to cause the blurry effect common in rainbows and I used a canvas lighter composite operation to enhance its realism.

That’s it!