JS1K 2017: some kind of magic

febuary 2017

there's now a Slack room dedicated to JS code-golfing:
You can fill this form to get an invite!

And a big thumbs up for Dwitter: a platform allowing to make art on a canvas in 140b...

Also, Dwitter inspired this entry by p01, and this 140b clone by nderscore.

JS1k is certainly the most important rendez-vous for JS code-golfers each year,
and this time a lot of people have been very busy to submit incredible entries with the theme "some kind of magic"!
Here are the 5 entries I worked on, in team and alone, plus some anecdotes and tricks we found while making them!

Mini Shadertoy

made with literallylara, nderscore, innovati, subzey, p01, sqaxomonophonen (and aemkei for the non-js1k version)


Our first team entry is inspired by our previous work on WebGL: MiniShadertoy, MiniShadertoyLite and WebGL playground.

We completely reworked these apps to include a split-screen UI, shareable URLs, a perfect compatibility with Shadertoy's syntax and a cool built-in example. (plus a fullscreen view on double-click).

As a result, our entry is able to play demos from (like Seascape), provided they only contain one shader code, and no extra buffers or channels.

Tricks and lessons learned:

PERIOD1k reloaded

made with innovati and subzey


Last year, we submitted PERIOD1k, a periodic table drawn on a canvas.
This time, we rewrote it entirely and the result is much better!
It's rendered in HTML/CSS and generated with a big chunk of ES6 template literals (26kb of generated code in total!)
It contains the definitive names for the last four elements (officialized in june 2016), better responsiveness, and a new information: the state of each element at room temperature (Liquid / Solid / Gas).

Tricks and lessons learned:

If the Moon was 1px

made with innovati


This demo is a tribute to If the Moon were only 1px by Josh Worth, reimagined with the hard constraint of 1kb.
We wanted to make an extremely complete and precise solar system, with all the planets, dwarf planets, satellites and belts.
We almost nailed it: all the planets positions are precise with a margin error < 1000px, and their size is pixel-perfect;
the major satellites have pixel-perfect positions and their full names are displayed in the page;
The only important size/accuracy compromises are the belts (not drawn in the page) and the minor satellites (placed at pseudo-random positions and not named).

Tricks and lessons learned:

Can I haz 1Karrot?

Individual entry


I always wanted to make something cool with CSS3D and emoji, but never had the good idea... until a week after the start of JS1k, when I realized I could make a game with a rabbit in a hat that could run through a maze to find its carrot, and with a surprising ending. All the other details were improvised during the next ~3 weeks of intensive development.

This demo relies heavily on two CSS and golfing experiments from 2016:
- JS games inputs (to support both arrow keys, WASD and ZQSD in 82 bytes);
- How to make games in CSS3D
(though all the "good practices" of this tutorial were heavily tortured and trespassed in this demo in order to save every byte possible).

Mobile controls would have been cool (moreover, mobile browsers have no problem displaying the demo), but it would have been so heavy to listen to touch events and bypass their default behavior to make the rabbit move (around 200b), that too many features would have been cut out (no animals, bad collisions, bad camera, less trees, etc.) and it was too much of a downgrade to me, so I abandoned it.

Same thing for the sound or the interaction with the animals: they were sacrified to make the main thing more enjoyable.

Big thanks to Keith Clark for his CSS3D expertise, and to the friends from the Codegolf Team for their tests!


I made a video recording to show the demo played in the best conditions (Windows 10 + Webkit):

Tricks and lessons learned:

WIP screenshots and funny glitches:

1Kind of magic

Individual entry


This demo was rushed during the last week of the compo, after seeing @literallylara's entry magic flute. (which has a remarkably readable source code).
As a joke, I asked "what if we played Queen's "A kind of magic" in JS? that'd be a fun interpretation of the theme!".
I ended up handwriting every note of the song and playing them with two JS oscillators (one for the melody and one for the drums starting after 12 seconds).
The remaining room was used to display some emoji at the center of the page, changing the background after each note, and using a little bit of JS speech synthesis to pronounce the first 3 lyrics of the song.
It was also a good occasion to finally make a non-mute demo for js1k!
The Speech Synthesis is inspired by miniSpeechSynthesis.

Tricks and lessons learned:

Bonus: CSS golfing

To finish, I'd like to talk about a code-golf trick that I found while making these demos, and it's about inline CSS code.

It generally saves 1 byte per element, but hey, every byte counts!

Inline CSS looks like "<div style='...'>", and as you may know, the attribute quotes can be omitted if the inline style doesn't contain any space, for example "<div style=color:#fff;background:#000;border-radius:50%>".

My demos contained many CSS properties that generally use spaces, and I discovered I could get rid of most of these spaces, by removing them or replacing them with a "+" or quotes (''). Here are some examples:

The only spaces that can't be removed so far are:

There's a second CSS trick that can save many bytes when you write a lot of CSS functions, but I don't really recommend it because it doesn't work on Safari: all the closing parenthesis at the end of CSS rules (or at the end of inline styles) can be omitted, ex:

To be concluded (after the results!)

Golfing the Game of Life (and its variations)

october 2016

We golfed the Game of Life many times in the past few years, in many different forms:

First, when still existed, we golfed the algorithm alone in less than 140 bytes (135b by aemkei and 126b by me);

Subzey joined us to golf miniGameOfLife, a complete Game of Life simulation in ASCII in 238b, which used the mouse and the keyboard to set the initial state and evolve;

Aemkei golfed a pure HTML version in 176b and he presented many... obfuscated... versions... on stage;

We made MiniGameOfBraille in 389b;

And finally, we ended up golfing a super tiny version on a 100x100px canvas inspired by aemkei's talks, in just 221b, featuring ES6 and a pseudo-random initial state.

<canvas id=a><svg onload=z=a.getContext`2d`;s=!setInterval`p=a;a=[];for(i=1e4;a[i]=s?3==k|p[i]&2==k:3<i%9,k=0,i--;)for((j)of[1,97,v,99])k+=p[i+j]+p[i-j];for(s=1e4;s--;)z[a[s]?'fillRect':'clearRect'](s%v,s/v|0,1${v=98}1)`>

During months, we really thought we couldn't do better than that. We even included this version in our js13kgames entry 26-games-in-1.

We also golfed other cellular automata like miniLangtonAnt, miniLangtonLoops and cellularandom.

This week, we discovered a new, intriguing variation of the Game of Life, that produced a Serpinski triangle fractal while it evolved from a vertical line.

So I tried to reproduce it and golf it from scratch with @sqaxomonophonen @subzey @nderscore and @veubeke. The result is HERE, and it fits in just 188b!

Here are the different steps of this quite epic golfing session, just after we realized that the rules of this game were the same as Game of Life's, except that cells live with 0 or 3 live neighbours instead of 2 or 3 live neighbours:

The commented source code can be found HERE.

// Just minified, not golfed yet.
<canvas id=a><script>d=[];k=0;setInterval(function(){a.width^=0;e=[];for(i=0;i<98*98;i++){if(!k){d[i]=((i%98)==49)?1:0;}else{n=0;for(j of[1,97,98,99])n+=!!d[i+j]+!!d[i-j];if(e[i]=[x=d[i],,x,!x][n])a.getContext`2d`.fillRect(i%98,~~(i/98),1,1);}}if(k)d=e;k++;},9)</script>

// Move k (a flag telling if the game has started or not) into d (an array representing all cells' state). Pass a string to setInterval. Move the rest of the code to remove some curly braces.
<canvas id=a><script>d=[k=0];setInterval(`a.width^=0;e=[];for(i=0;i<98*98;i++){n=0;if(k)for(j of[1,97,98,99])n+=!!d[i+j]+!!d[i-j];else d[i]=e[i]=((i%98)==49)?1:0;if(e[i]=[x=d[i],,x,!x][n])a.getContext("2d").fillRect(i%98,~~(i/98),1,1);}d=e;k++`,9)</script>

// Move some code inside e (an array representing the new state of the grid). Use .map() to iterate on the set of neighbours offsets. 
<canvas id=a><script>d=[k=0];setInterval(`for(e=[a.width^=i=0];i<98*98;i++)n=0,k?[1,97,98,99].map(j=>n+=!!d[i+j]+!!d[i-j]):d[i]=e[i]=i%98-49?0:1,(e[i]=[x=d[i],,x,!x][n])&&a.getContext("2d").fillRect(i%98,~~(i/98),1,1);d=e;k++`,9)</script>

// Reverse the loop (from 10000 to 0) to iterate on all the cells. Move n reset at the end.
<canvas id=a><script>d=[k=0];setInterval(`e=[a.width^=0];for(i=1e4;i--;k?[1,97,98,99].map(j=>n+=!!d[i+j]+!!d[i-j]):d[i]=e[i]=i%98-49?0:1,(e[i]=[x=d[i],,x,!x][n])&&a.getContext("2d").fillRect(i%98,~~(i/98),1,1))n=0;d=e;k++`,9)</script>

// Instead of incrementing k at each loop, just set it to the value of e after the first iteration
<canvas id=a><script>     d=[k=0];setInterval(`e=[a.width^=0];for(i=1e4;i--;k?[1,97,98,99].map(j=>n+=!!d[i+j]+!!d[i-j]):d[i]=e[i]=i%98-49?0:1,(e[i]=[x=d[i],,x,!x][n])&&a.getContext("2d").fillRect(i%98,~~(i/98),1,1))n=0;k=d=e`,9)</script>

// Place all the JS code in an svg onload. Reorder the code a little, and simplify the game's rules (e[i]=!(d[i]?n^2&n:n^3) instead of e[i]=[x=d[i],,x,!x][n])
<canvas id=a><svg onload="d=[k=0];setInterval('e=[a.width^=0];for(i=1e4;i--;k?[1,97,98,99].map(j=>n+=d[i+j]+d[i-j])|(e[i]=!(d[i]?n^2&n:n^3))&&a.getContext`2d`.fillRect(i%98,i/98|0,1,1):e[i]=i%98==49)n=0;k=d=e',9)">

// Set d to zero and use it as the second parameter of setInterval (it's not useful as an array at the beginning ant it's set to the value of e after each iteration)
<canvas id=a><svg onload="setInterval('e=[a.width^=0];for(i=1e4;i--;d?[1,97,98,99].map(j=>n+=d[i+j]+d[i-j])|(e[i]=!(d[i]?n^2&n:n^3))&&a.getContext`2d`.fillRect(i%98,i/98|0,1,1):e[i]=i%98==49)n=0;k=d=e',d=0)">

// k is not needed anymore, the truthiness of d is enough to choose between "initiate the grid" (when d=0) and "iterate" (when d=e)
<canvas id=a><svg onload="setInterval('e=[a.width^=0];for(i=1e4;i--;d?[1,97,98,99].map(j=>n+=d[i+j]+d[i-j])|(e[i]=!(d[i]?n^2&n:n^3))&&a.getContext`2d`.fillRect(i%98,i/98|0,1,1):e[i]=i%98==49)n=0;d=e',d=0)">

// Do not floor the y position of each pixel (at this scale, it's almost not noticeable)
<canvas id=a><svg onload="setInterval('e=[a.width^=0];for(i=1e4;i--;d?[1,97,98,99].map(j=>n+=d[i+j]+d[i-j])|(e[i]=!(d[i]?n^2&n:n^3))&&a.getContext`2d`.fillRect(i%98,i/98,1,1):e[i]=i%98==49)n=0;d=e',d=0)">

// Try to use a boolean in d... but it doesn't work on Firefox (Fx needs an integer as second param of setInterval)
<canvas id=a><svg onload="d=!setInterval('e=[a.width^=0];for(i=1e4;i--;d?[1,97,98,99].map(j=>n+=d[i+j]+d[i-j])|(e[i]=!(d[i]?n^2&n:n^3))&&a.getContext`2d`.fillRect(i%98,i/98,1,1):e[i]=i%98==49)n=0;d=e')">

// Rollback
<canvas id=a><svg onload="setInterval('e=[a.width^=0];for(i=1e4;i--;d?[1,97,98,99].map(j=>n+=d[i+j]+d[i-j])|(e[i]=!(d[i]?n^2&n:n^3))&&a.getContext`2d`.fillRect(i%98,i/98,1,1):e[i]=i%98==49)n=0;d=e',d=0)">

// Move some code inside e[i]=xxx (e[yyy,i]=xxx is valid in JS!)
<canvas id=a><svg onload="setInterval('e=[a.width^=0];for(i=1e4;i--;e[[1,97,98,99].map(j=>n+=d[i+j]+d[i-j]),i]=d?(d[i]?n^2&n:n^3)?0:!a.getContext`2d`.fillRect(i%98,i/98,1,1):i%98==49)n=0;d=e',d=0)">

// Move some code (declaration of e and reset of a.width) inside the for loop and mix it with "i=1e4". Also, save the number 98 in a variable (k) to reuse it many times.
<canvas id=a><svg onload="setInterval('for(a.width|=e=[i=1e4];i--;e[[1,97,k=98,99].map(j=>n+=d[i+j]+d[i-j]),i]=d?(d[i]?n^2&n:n^3)?0:!a.getContext`2d`.fillRect(i%k,i/k,1,1):i%k==49)n=0;d=e',d=0)">

// Simplify the rules with a global "n xor"
<canvas id=a><svg onload="setInterval('for(a.width|=e=[i=1e4];i--;e[[1,97,k=98,99].map(j=>n+=d[i+j]+d[i-j]),i]=d?n^(d[i]?2&n:3)?0:!a.getContext`2d`.fillRect(i%k,i/k,1,1):i%k==49)n=0;d=e',d=0)">

// Moved rules inside of height argument of fillRect.
<canvas id=a><svg onload="setInterval('for(a.width&=e=[i=1e4];i--;a.getContext`2d`.fillRect(i%k,i/k,1,e[i]=d?!(d[i]?n^2&n:n^3):i%k==49))[1,97,k=98,99].map(j=>n+=d[i+j]+d[i-j],n=0);d=e',d=0)">

// Simplify the rules again (reuse the global "n xor" trick) and simplify "i%k==49" into "i%k^49".
<canvas id=a><svg onload="setInterval('for(a.width&=e=[i=1e4];i--;a.getContext`2d`.fillRect(i%k,i/k,1,e[i]=!(d?n^(d[i]?2&n:3):i%k^49)))[1,97,k=98,99].map(j=>n+=d[i+j]+d[i-j],n=0);d=e',d=0)">

// Remove the quotes of the svg onload attribute, and replace the map() with "for(j of[...])". The space is replaced with a vertical tab character (considered as a space in JS but not in HTML)
<canvas id=a><svg onload=setInterval('for(a.width&=e=[i=1e4];i--;a.getContext`2d`.fillRect(i%k,i/k,1,e[i]=!(d?n^(d[i]?2&n:3):i%k^49))){n=0;for(jof[1,97,k=98,99])n+=d[i+j]+d[i-j]}d=e',d=0)>

// Move n=0 at the beginning of the loop to get rid of two more curly braces.
<canvas id=a><svg onload=setInterval('for(a.width&=e=[i=1e4];n=0,i--;a.getContext`2d`.fillRect(i%k,i/k,1,e[i]=!(d?n^(d[i]?2&n:3):i%k^49)))for(jof[1,97,k=98,99])n+=d[i+j]+d[i-j];d=e',d=0)>

With this extremely small code, we tried to re-golf miniGameOfLife with the same principle, and got it down from 221b to 183b! The result is HERE!

<canvas id=a><svg onload=setInterval('for(a.width&=e=[i=1e4];i--;a.getContext`2d`.fillRect(i%k,i/k,1,e[i]=d?d[i]+n==4||n==4:3<i%9))for(jof[n=1,97,k=98,99])n+=d[i+j]+d[i-j];d=e',d=0)>

(to save one extra byte, Subzey also had the idea to rewrite the rule "e[i]=(d[i]&&n==2)||n==3" into "e[i]=d?d[i]+n==4||n==4")

To conclude, I'd like to show that this model can actually be used as a bootstrap for any Game of Life variation, not only these two:
<canvas id=a><svg onload=setInterval('for(a.width&=e=[i=1e4];i--;a.getContext`2d`.fillRect(i%k,i/k,1,e[i]=d?[RULES]:[INITIAL STATE]))for(jof[n=1,97,k=98,99])n+=d[i+j]+d[i-j];d=e',d=0)>

Replace [RULES] with the conditions that make e[i] (the current cell) alive based on d[i] (the previous state of the current cell) and n (the number of live neighbours);
and replace [INITIAL STATE] with the code that defines the initial state of the grid, based on i (the cell counter).

For example, if you use the rules of Life without Death, you can get something like this:

the Team

Web Speech API golfing (plus our new best JS golfing tricks)

october 2016

The Codegolf Team and I constantly make new codegolf-related discoveries, sometimes completely randomly.

Our most recent ones are the following:

We also discovered the Web Speech API, allowing to make your browsers generate voices from any text you input (Speech Synthesis) and vice-versa (Speech Recognition)!

Naturally, we tried to golf these new features as much as possible, and here is the result:

And as many people are curious about how we make things like that, here's the detail, for miniSpeechSynthesis, golfed by @p01, @subzey, @nderscore, @veubeke, @literallylara, @sqaxomonophonen and I:

// Non golfed
<input id=i><button onclick="s=speechSynthesis;u=new SpeechSynthesisUtterance(i.value);u.voice=s.getVoices()[0];s.speak(u)">Speak!

// Use new() to avoid using a space, and remove attribute quotes
<input id=i><button onclick=s=speechSynthesis;u=new(SpeechSynthesisUtterance)(i.value);u.voice=s.getVoices()[0];s.speak(u)>Speak!

// Add speechSynthesis's scope to get rid of "s."
<input id=i><button onclick=with(speechSynthesis)u=new(SpeechSynthesisUtterance)(i.value),u.voice=getVoices()[0],speak(u)>Speak!

//Use ES6 destructuring to set u.voice to getVoices()[0] implicitly
<input id=i><button onclick=with(speechSynthesis)u=new(SpeechSynthesisUtterance)(i.value),[u.voice]=getVoices(),speak(u)>Speak!

// Move a bit of previous code in empty parenthesis
<input id=i><button onclick=with(speechSynthesis)[u.voice]=getVoices(u=new(SpeechSynthesisUtterance)(i.value)),speak(u)>Speak!

// Get rid of the button (speak when the input changes), the voice and the id "i" because it's implied now
<input onchange=speechSynthesis.speak(new(SpeechSynthesisUtterance(value)))>

// Use the onblur event (shorter and more handy: indeed, triggering the speaker at each keystroke was not nice)
<input onblur=speechSynthesis.speak(new(SpeechSynthesisUtterance)(value))>

// Use a VT character between "new" and "SpeechSynthesisUtterance"
<input onblur=speechSynthesis.speak(newSpeechSynthesisUtterance(value))>


The other apps use very similar techniques as you can see for example in the source code of miniSpeechRecognition (webkit only):

<svg onload='with(new webkitSpeechRecognition)start(onresult=e=>write(e.results[0][0].transcript))'>

...or its bgColor variant, using the trick described at the beginning of this article:
<svg onload='with(new webkitSpeechRecognition)start(onresult=e=>bgColor=e.results[0][0].transcript)'>

... and as a bonus, an app that repeats with Speech Synthesis what you say in the microphone, interpreted by Speech Recognition! The lite version is only 146b (webkit only)
<svg onload='with(new webkitSpeechRecognition)start(onresult=e=>speechSynthesis.speak(new SpeechSynthesisUtterance(e.results[0][0].transcript)))'>


Update 01/17: a nice article on explains these tricks with more detail.

How to make a game in CSS3D

october 2016

This article is aimed at developers that are already familiar with JS game making and CSS3D.

If you don't already know CSS3D, I also recommend reading the following resources:
- An Introduction to CSS 3D Transforms
- Things to watch when working with CSS3D
- Keith Clark's articles and demos about CSS3D engine, lighting, shadowing, etc.
- Super Mario Kart in CSS, it's heavy, old, not optimized, not cross-browser, but that's what inspired me to do this today :)

I won't cover all the features of CSS3D, but instead I'll focus on some discoveries I've made, like how to deal with the rotation and translation axis, how to simulate a camera and how to make CSS3D scenes interactive with a little bit of JS.

In parallel with this article, I'm updating this Github repo with my experiments.


Why make games in CSS3D? Because it works fine (most of the time), because it's fun and hacky, and also because it's super lightweight! You can literally make a 3D scene interactive with a few lines of HTML, CSS and JS (example) (demo), so it can be useful for lazy coders, or code-golfers trying to work with the size limits of js1k or js13kgames!

Spoiler: 3D games often are just 2D games plus perspective

It seems stupid to say it like that, but it's one of the most important things I realized while experimenting: if you can make a 2D game in JS (for example a chess game or a racing game seen from above, or anything else you can imagine), you literally just need to look at your scene from a slightly different angle and you have turned it into a 3D game "for free".
You can see what I mean with this Mario Kart prototype: the only real difference between the 2D view and the 3D view is rotation of 80 degrees along the X axis!

But we'll see that in greater detail later.

So let's get started with CSS3D!

Nowadays, CSS3D is pretty easy to use. Modern browsers don't require vendor-prefixes anymore and their performances are... decent.

All you have to do is set "perspective: 400px" to a container in your page (avoid the <body> though) - of course you can replace 400px with any length you want; as far as I understand, this length represents the distance between the container and the virtual camera that will "see" the scene in perspective. (the lower the perspective, the more deformed the scene is). Then fill the container with elements, apply all the CSS3D transforms you want to these elements, don't forget to set "transform-style: preserve-3d;" to render them in "real 3D" (otherwise, the browsers will flatten all the scene and make it look as if it's in perspective, but there would be no depth at all in reality), and you're done!

But now, let's dig into some subtleties that you may face while playing with this new toy!

Subtlety #1: HTML elements vs. axis, translations and rotations?

By default, each HTML element of your page is drawn at a certain position according to the flow of the page and the CSS you applied to the element, its neighbours and its ancestors. It has no translation and no rotation, and its default transform-origin it at their top-left corner. This transform-origin is editable, and it's the starting point of all the possible CSS transforms, but six of them are particularly important here: rotateX / Y / Z and translateX / Y / Z. The X axis goes on the right, the Y axis goes downwards, and the Z axis goes in depth towards your eyes, as you can see in the following demo:


The demo above allows to move the transform-origin of a square (represented by a red dot), and allows to translate in X,Y and Z axis and rotate along X,Y and Z axis, in this order. All the range inputs go from 0 to 100 (regardless to the units used).

Note that translateX and translateY can be expressed in percent units, but not translateZ. If you try to sate a translateZ in %, the whole transform will be discarded.

Note also that the order of the transforms is extremely important. For example here, you can translate the square then rotate it, but you wouldn't get the same result if you rotated the square before translating it. Transforms are accumulated from the first one to the last one, while the transform-origin can only have one value used by all the transforms. (of course, even if its value never changes, the transform-origin moves with the element when it's translated).

This principle allows, for example, to rotate an element, then translate in, then rotate it back to the original angle, to make an object move around a circle!

Finally, note that the X, Y and Z axis of an element can have different orientations than the ones by default if a parent element is rotated. (for example, if the parent of the square above had a 90 degrees rotation along its X axis, the square would undergo this parent transform and have its Y and Z axis rotated 90 degrees along the X axis of the parent, i.e. the Y axis would behave like a Z axis and the Z axis would behave like X axis.

The other possible CSS transforms include matrix, translate, scale, scaleX, scaleY, skew, skewX, skewY, matrix3d, translate3D, scale3D, rotate3D and perspective, but we won't study them here. ( for the record, matrix3D has very interesting applications!)

Subtlety #2: The HTML structure

Even if a container (with perspective) and its children (with CSS transforms) are enough to test CSS3D, a good HTML structure will make your work (and calculations) much easier, especially if you want to do a CSS3D game. Here's the one I recommend:

First, you need a viewport (the equivalent of a canvas element when you do a canvas-based JS game). For example, use a 600x400px div. The perspective will be applied to this element.

At the center of this viewport, we place a 0x0px div called "camera". It's not REALLY the camera (more details in the next paragraph), but the point that will always be watched by the camera, something like a global transform-origin for the game.

Inside this "camera" point, we can place a scene container with a defined size and inside it, all the scene objects in their order of appearance (for a better browser support - more details at the end of the article). For example, we can put inside the kart, the tree, and finally the circuit.

Here's the template's HTML:

<div id=viewport>
  <div id=camera>
    <div id=scene>
      (...scene content...)

And CSS:

* { transform-style: preserve-3d; box-sizing: border-box; }
#viewport { width: 600px; height: 400px; overflow: hidden; perspective: 400px; }
#camera { width: 0px; height: 0px; position: relative; left: 300px; top: 200px; }
#scene { width: 4000px; height: 4000px; transform-origin: 0 0; }
/* scene content... */
#scene { transform: rotateX(80deg) translateZ(-70px); }

All your scene objects can be placed in the scene the same way you'd place them in 2D on a HTML page (using left/top, or margins, or translateX/Y, etc.).

The last CSS line (rotateX and translateZ on the #scene element) enables the 3D view, exactly like in the previous Gif. The translateZ is optional, its goal is to place the camera a little above the scene, and avoid some bugs (more details later).

You can find a demo HERE.

In this demo, the viewport is shown in red, the camera in blue, and the scene in green (you can see the top-left corner of the scene in perspective).

In the screenshot below, you can see what the template looks like before and after adding the last line of CSS (the 3D toggle)

That's all! Everything else depends on your content and your creativity!

Subtlety #3: A "camera"?

In a webpage, there's no "camera", but there's a viewport (the portion of the page visible in browser's window).

In most cases, and even in 2D games, the notion of camera is completely neglected, and we generally call "camera" the current view, i.e. the rectangle in which the user can see your game.

But in 3D, we should never forget that the camera is a point in the space, and the scene is "seen" from this point.

Remember, when you make a scene in CSS3D, you set a given perspective to a container, and it defines how all the children are rendered. Well, here it is! If your scene has a perspective set to "400px", the camera is just 400px away, along the Z axis. Its a virtual point in the air between your screen and your eyes.

So, can you guess what happens if you perform a "translateZ(400px)" transformation on your scene?

Yep, what you get is a first-person view!

Here you can see the translateZ(400px) being enabled and disabled in the Mario Kart prototype (there's also a toggle of the kart's opacity), and of course, you can have this transformation always enabled if you make a CSS3D FPS for example!

Subtlety #4: Interactivity!

Adding interactivity (controls) to a 3D scene is rather simple if you're well organized and keep a few trigonometry basics in mind:

(NB: the following tips mostly apply to CSS3D games with first-person or third-person view, like a FPS or Mario Kart.)

For example, you may want to look around you using the mouse or the keyboard, so you can declare a var called angle_z, and update it every time you move the mouse or press Left/Right arrows.
For more simplicity, this var will contain your Z rotation in radians (one turn = 2 * PI ~= 6.28 radians). It's possible to use degrees but all the JS Math functions work in radians and you'd need to convert units all the time, so I don't recommend it.

You'll probably want to move too, that's why you can declare two vars x_pos and y_pos. If you're surprised by the "Y" axis used to walk forwards and backwards, remember that our 3D game starts as a 2D game, and in 2D, the Y axis allows you to move up and down, which will become a forwards-backwards movement after passing in 3D view. These vars can be updated with keyboard inputs for example.

Remark: naively, we can think that it's okay to move along the X axis when we press left and right keys, and move along the Y axis when we press up and down keys, but in first-person or third-person view, it's often not what we want at all. What we want is to go forwards according to our current angle! So when we press "up", the right thing to do is probably something like:

pos_x += walk_speed * Math.sin(angle_z);
pos_y += walk_speed * Math.cos(angle_z);

Finally, we want to apply these vars to our scene at each frame, and to do so, we just have to generate a CSS transform string and apply it with JS; something like: = "rotateZ(" + angle_z + "rad) translateX(" + pos_x + "px) translateY(" + pos_y + "px)";

All the rest is just a slight variation of this principle. For example, to place the kart at the right position and the right angle, in front of the camera, you have to do something like: = "translateX(" + (-pos_x - kart_width / 2) + "px) translateY(" + (-pos_y - kart_width / 2) + "px) translateZ(" + kart_height + "px) rotateZ(" + (-angle_z) + "rad)";

It takes a few fails and retries to get the right values and the right order for all the transforms, but with practice it becomes more and more logical and natural.

Hack: if you're super lazy, your third-person sprite (for example the kart) can be placed over the viewport! You can see a demo HERE. The illusion is perfect... until something passes between the subject and the camera...

Subtlety #5: browser support?

I said earlier that CSS3D works fine... most of the time. Indeed, you may encounter a few glitches, or even some elements that disappear completely while they should be here, but that's the fault of the browsers, and Firefox in particular.

First, some parts of the scene can look broken if the camera is too close to them. It happens with the road for example, as you can see during a few frames of the previous GIF. This happens on both Chrome and Firefox, but for their defense, my camera was VERY close to the road (like, 20px high). The solution is to put the camera a bit higher.

Then, with the great help from Keith Clark, I understood why Firefox hides the kart behind the scene in this case:

As you can see in his super detailed pull request, the order of the elements in the DOM is super important to help the browsers draw the "above" elements above, and the "below" elements below, and not the other way around. He also explains that Firefox completely stops doing any depth-ordering effort if your scene contains more than 100 children. In that case, you need to reorganize your DOM manually at each frame... or more realistically, just completely drop Firefox support for your game!

Happy game making! If you have questions, feel free to get in touch on Twitter, Gitter or Slack!


Some subtleties of keyboard inputs in JS games

september 2016, january 2017

After JS13kGames 2016, we realized on Slack that many games were only playable with W, A, S, D keys, and many players were bothered by that.

In this article I'll cover this point plus a few other subtleties of keyboard input in JS, and provide a tiny library that you can drop directly in your future games to get these problems out of the way.

The WASD issue

WASD keys can be used as an alternative for arrow keys in a vast majority of keyboards worldwide. All QWERTY and QWERTZ keyboards (and some hybrid keyboards like QWERTY/russian for example) support this pattern just fine. But there's another layout widely used too (especially in France, Belgium and Canada): AZERTY. On this kind of keyboard, WASD becomes ZQSD. Other minor layouts exist but we can ignore them for now.

So the idea is to teach game developpers to natively support not only WASD, but also ZQSD AND arrow keys! Why support only one scheme when we can please everyone with a super light overhead?

To sum up, up arrow must be aliased with W and Z, left arrow with A and Q, right arrow with D, and down arrow by S.

The JS keyboard events issue

As you can see on this page displaying the keyCodes of all your keyboard events, when you press a key, two events are fired by the browser: keydown and keypress. when you release it, another event is fired: keyup. And if you keep a key pressed for a moment (depending on your OS settings), after a short pause during which nothing happens, both keydown and keypress are fired repeatedly. There's also an "input" event, but it happens only in form elements, so it's not really relevant here.

There are many problems with this default behavior: firstly, this pause that happens after pressing a key is not good for a video game. If you play a platform game, you don't want Mario to stay idle for a few frames before running or jumping. And secondly, the keypress event is totally messed up (look at the keyCode returned by this event when you press letter keys or arrow keys, and you'll see absurd values almost everytime, and different absurd values depending on the browser you're using.

So my advice is to avoid relying on keypress events altogther, and also to avoid relying on keydown at each frame to see if a key is down or not, because the pause will bother your players.

The solution

So here's a super short solution to all these issues.

It introduces four global boolean variables to keep in memory the state of each direction.

Of course, feel free to fork it and replace them with non-global vars if you want, but for this example, I'll just keep things simple, and who cares about global vars in code-golfing anyway? Okay, maybe everyone cares, but not me! :p

So here's the code:

// Keys states (false: key is released / true: key is pressed)
up = right = down = left = false;

// Keydown listener
onkeydown = (e) => {

  // Up (up / W / Z)
  if(e.keyCode == 38 || e.keyCode == 90 || e.keyCode == 87){
    up = true;
  // Right (right / D)
  if(e.keyCode == 39 || e.keyCode == 68){
    right = true;
  // Down (down / S)
  if(e.keyCode == 40 || e.keyCode == 83){
    down = true;
  // Left (left / A / Q)
  if(e.keyCode == 37 || e.keyCode == 65 ||e.keyCode == 81){
    left = true;

// Keyup listener
onkeyup = (e) => {
  // Up
  if(e.keyCode == 38 || e.keyCode == 90 || e.keyCode == 87){
    up = false;
  // Right
  if(e.keyCode == 39 || e.keyCode == 68){
    right = false;
  // Down
  if(e.keyCode == 40 || e.keyCode == 83){
    down = false;
  // Left
  if(e.keyCode == 37 || e.keyCode == 65 || e.keyCode == 81){
    left = false;

Then, during you game loop, you just need to rely on the state of these four variables to know if the player is currently pressing a direction (or an alias in WASD or ZQSD) or not!

Want to see a demo? Of course! Just launch the first level of my js13kgames entry. It uses this exact technique :)

P.S: if you want a minified ang golfed version, here it is: only 160b! (the four vars u,r,d,l, are not booleans but just truthy and falsy)

u=r=d=l=0;onkeydown=(e)=>t(e,1);onkeyup=(e)=>t(e);t=(e,v,l,i)=>{for(i in l={u:[38,90,87],r:[39,68],d:[40,83],l:[37,65,81]})if(l[i].includes(e.keyCode))top[i]=v}

P.P.S: Here's a 122b version by @p01, using a radically different idea: a look-up table of keyCodes represented as a string, and ".which" instead of ".keyCode" (because it's the same thing):


january 2017 golfing

I made a new version that's only 87b, but it works if the only keys you need to support are arrows, WASD and ZQSD (most of the other keys may collide with one of the four directions):


It fell down to 85b with the help of nderscore:


then 83, by merging the two event listeners:


and finally 82 with Subzey's touch:



Note: there's no collision with the keys "E", "R", "T" "space", "shift" and "Enter", so you can also handle them just fine with just a 100 bytes:





JS13kGames 2016: Super Chrono Portal Maker

august-september 2016


You can play the game HERE! (please share!)

Try also my Codegolf Team's entry "26 games in 1" HERE (please share too)!

You can see all the shared levels (and share yours) HERE.

You can follow the official account @SuperCPMaker HERE.

The Github repo of the game is HERE.

The speedruns of the game are HERE. (warning: spoilers!)

The game was ranked 3rd in the Desktop category!


This year, I was really into 2D platforming.

I developed a platform engine during my free time.

We also discussed on JS13k's slack about the idea of a "13kb Mario Maker". This idea made me develop a 1kb level editor with shareable URLs, that all js13k's devs can use in their games...

As I was a bit rusty on 2D maths, I also made this trigonometry cheat-sheet for 2D games that many people found very useful.

I also made a tiny graphics editor, to help devs produce line-based graphics quickly and lightly (like I did for my 2015 entry GeoQuiz).

And finally, @innovati, @p01 and I, made a graphical music composer called miniPiano that lets you draw a melody and outputs a tiny piece of JS code to play it back.

Everything is ready to make a cool game now!

Week-end 1

When JS13kGames 2016 started, I remembered this Mario Maker idea and decided to mix it with two other of my favourite games, Portal (no presentation needed) and Chronotron (a Flash game that lets you rewind time and collaborate with your past self to solve puzzles).

The idea of mixing Mario and Portal also comes from Super Mario Portal by "Moumou" (20Mb, made with RPG Maker!) and Mari0 by StabYourself.

The idea of mixing Portal and time travel also comes from Thinking with Time Machine, a great Portal 2 mod.

Some inspiration can also be found in Braid (for time travel) and Enough Plumbers (for the multiple Mario gameplay).

And for the record, this article is a great resource about 2D portals implementation!

So I wanted to make a game featuring most of the cool mechanics of these three games (Mario, portal gun, time travel, mechanisms, companion cube, traps, and of course a level editor), but also being something completely unseen before.

The theme of js13k this year is "Glitch!", and to include it in my game, I decided to provoke glitches in case of time paradoxes... (and in a few other cases).

Like previous years, my personal challenge was to make a game so big that it would be complicated to make it fit in 13kb. So with this puzzle-maker idea, I believed it would be a hard (and fun) task to achieve. I really wanted to make a game that was a complete system with a lot of interesting rules, and with a quality high enough that no player would guess that its size was limited (and make them wonder how all that can fit in 13kb).

Unlike previous years, my source code before minification will be really readable: well commented, cut into multiple files, and with explicit variable names! I'll do all the optimizations at the end instead. I can't promise that the code will be clean and mainainable, but at least, it'll be readable.

Last detail: last year, half of the zip was used by the game's data (the world map, the names of countries, capitols, famous places, etc), but this year, I'll really focus on filling it with a lot of code and a ton of features, instead of data. So, to sum up, I don't wanna look lazy this year...

During this first week-end, I wrote the game's specs, then I drew all the images of my game into this spritesheet...

... of course, inspired by Super Mario Maker, Chronotron and Portal, but without the copyrights:

I also developed basic menus (title screen, level selection screen...) and started to develop the main features of the level editor (select tiles, place them on the map, etc).

All the following screenshots show the interface at the end of the week-end...

Current code size: ~7kb commented, ~1.5kb minified and gzipped. (plus a 1.8kb PNG)

Week 2

During the first half of this week, I developed the level editor, to give it strict rules (eg. only one starting point, only one end flag, the ability to place green pipes and switches that make them go up and down, the ability to place pairs of balanced platforms, etc...).

To do that, I had to define a precise format for saving the levels, a format that allows to store all the level's tiles in a grid of 40 * 20 ASCII characters (which is the most compressible output I could find while developing the 1k level editor), plus information about linked elements (pipes and switches, pairs of balanced platforms, etc). And all this data needs to be serializable in a string shorter than 4096 bytes, in order to be easily shareable via an URL on twitter.

Here's what the data of a level looks like:

// As a JS object:
level_data = {
  tiles: [
    [0, 0, 1, 2, ...],
    [3, 2, 0, 0, ...],
  pipes: [
    [x, height_1, height_2, switch_x, switch_y],
  balances: [
    [x1, y1, x2, y2],

// As an URL:

I also added many checks in the code of the editor to prevent to draw overlapping pipes and balances, and also to avoid overwriting important elements of the map (like the pipes or the time machine) to avoid bugs.

The whole code, including the menus, the complete level editor, as well as the "share", "clear" and "exit" buttons, is 19kb commented, and 2.16kb minified and gzipped. (plus the usual 1.8kb PNG)


At the end of the week, I designed the first levels of the game, which will be useful for me, to develop the game's engine in "real conditions".

Weeks 3 & 4

During these two weeks I wrote the game's engine, little by little. Here's my TO DO list, plus some GIFs I tweeted while developing it:

General stuff:

Basic controls:

(at this point (and after a little refactoring session), the JS code fits in ~3.6kb minified and gzipped. The zip takes 5.4kb.)


(Little refactoring... current zip size with 5 levels: 6.5kb)


(Huge refactoring (it took 5 days)... current zip size with 10 levels: 8.2b)

Time travel:

(Another refactoring...current zip size with 15 levels: 10.5kb)

Weeks 5 & 6

These two weeks (actually, one week plus two days) were consacred to the creation of the final levels included in the game, the music, the debugging and the polishing:

(current zip size: 13.9kb)

There's an important discovery made a few days before the deadline: Advzip, a zip optimizer/recompressor is able to save around 1kb out of a 13kb zip. By passing my current zip through Advzip, it only takes 12.8kb. We're still below the limit! Maybe we can have some music...

At this point, my commented source code weighs 114kb (scattered in 12 files and containing more than 3600 lines of code), and shrinks to 72.4kb when minified (including 27kb of level data)

And yes, as incredible as it sounds, once zipped, all that code fits in less than 13kb!


Zip size after this golfing: 11.1kb!

Final touch

With 24 hours left and nearly 2kb available in my zip, I called Anders Kaare to save my life once again. He made me a great collection of sounds plus a gorgeous musical theme in no time!

I also added a hidden, super difficult level and recorded all my developer times as a challenge for speedrunners.

All these latest details were added in a hurry, without any form of optimization or golfing.

Final beta-testing with my colleagues!

Final interface:


This project took me nearly 200h to complete, much more than any of my previous entries, and more than any of my previous personal projects!

There's a lot more that could be done, but I ran out of time and energy... but never ran out of free space! My final zip is just 12.5kb!

I'm still very happy with the result, and hope that a lot of people will enjoy it and create stunning levels!

Thanks js13kgames, and Andrzej, and Anders for the music, and all the people that helped me and supported me during this compo, and see you next year for something even bigger! :D

One week later:

People play and enjoy the game, they also start making cool levels, and they speedrun it too! That's great!

I also took the time to speedrun it as fast as I could (better than my own "dev times" in the game), and you can see the result here (warning, spoilers!):

But even if you create something and think you master it, someone will always be better than you, and it's just awesome!

Two weeks later:

Publication of the final results:

- Super Chrono Portal Maker was ranked 2nd in Community votes. 26 games in 1 was ranked 10th! Thanks guys!

- Super Chrono Portal Maker was ranked 3rd in the Desktop category (and 26-games-in-1 54th)! Thanks jury!

Also, these playtests by Jupiter Hadley:

- Super Chrono Portal Maker

- 26-games-in-1

2017 update: A new speedrun in 6:18.40!


EQCSS: The Search For The Holy Grail: How I Ended Up With Element Queries, And How You Can Use Them Today

july 2016

A great article written by @innovati about our Element Queries library got published on Smashing Magazine!

You can read it HERE!

Shitty systems & superfluous hacking fatigue

or: How we waste most of our lives struggling to obtain the most basic features from overly complicated and badly designed systems

june 2016

For once, this isn't a regular front-end blog post. It's a rant post, complaining about the fatigue of having to hack our way through the shitty systems that surround us more and more everyday. Heck, I'm an old engineer, I'm used to shitty systems. My work even consists in dealing with some of the most shitty systems out there: Web browsers. But today, I'll complain about what they call "smart" phones. Warning: curses inside.

I won't mention my own phone, as it's a Lumia 735 that comes from a batch built with some shitty component that makes it reboot randomly, and which is not even capable of installing or updating any application since I (painfully) installed Windows 10 on it. So I'm already screwed in this domain.

But as every engineer, I have relatives that think it's my job to "know these things" and my passion to make them work.

Anyway, my mom has a relatively new Samsung Galaxy S5 Mini smartphone, running on Android 4.4. She makes a very basic usage of it, besides calling people: she takes a ton of pictures, "edits" them through the Google Photos app and posts them on Facebook.

It all began when she wanted "more memory", translate: more space to store the photos on the phone, even if they're backed up in the cloud. Sure, whatever, a 64GB Samsung microSD card is less than 25€ nowadays, so let's buy one and install it.

Rightaway, I try to use my PC to transfer all the photos from the internal 16GB memory to the microSD card... For some reason, i'm not allowed to do it, and have to use the embedded shitty file explorer to do it directly on the phone. It takes hours, and in the process, changes the creation date of every file to the present day, which results in many albums being mixed up in Google Photos. Screw you, file explorer.

Fast forward a few days, I get a complaint that the phone freezes every couple hours, and that Google Photos pops a write error everytime we try to edit a photo and save it. And since I'm the last one who touched it, It's my fault, obviously.

So I take the phone back and try to figure out what the fuck is shittening... After a long search, it turns out this version of Android has a default setting that locks writes on the MicroSD card for most non-native applications, for the user's safety or some shit like that. That's why the default camera app could take pictures and save them on the card, but Google Photos, installed via the Play Store, couldn't edit them in place. And even if I have no proof, I'm pretty certain that Google Photos provoked some sort of memory leak because of this card lock, which was responsible of the freezes. But I digress. Also, I won't mention the fact that an OS made by Google forbids an application made by Google to write on my SD card for my safety, because fuck logic.

Before going further, I take a look at the "firmware update" page of the phone settings, which tells me that a 900MB update to Android 5 is downloaded and ready to install, but every attempt to start the upgrade fails lamentably and there's no solution. WHATEVER.

Okay so how can I disable this default SD lock on 4.4? Turns out, it's just a matter of setting a value to "true" in a XML file stored in the system partition of the phone. But it would be too simple if this partition was visible with a file explorer or a PC. No, I have to install one of the dozens "SD unlock" apps of the Play Store, which will do that for me.

But here's the joke, all those apps weigh a few MB and require something called "root access". Yes. All that, just to edit a fucking line in a fucking XML file. Okay whatever Google, tell me how I can root your shitty OS, I'm tired now.

Rooting an S5 Mini, for some reason, is not as easy as any other fucking Android phone. On this one, the regular rooting solutions fail, and tell me to try somewhere else, generally redirecting me to a Windows software with better success rates.

I plug the phone to my laptop using the mini-USB data cable of my Windows Phone (that's the only one I have on me)... Error: Unrecognized peripheral. Moving to the USB3 port... it works. ah, no. Ah, yes. Ah, no. this motherfucker blinks, and alternates between connected and disconnected every second. The fuck! Root tutorials tell me to install a fork of the Samsung USB drivers without some shitty adware that they include in every exe... impossible to find a live download link. Fuck it, I install the official drivers. It changes nothing. FUCK. Just to be sure, I borrow a genuine Samsung mini-USB data cable... oh. well, it seems to work, but I wouldn't call it stable (still makes some disconnection blinks every minute or so.) Modern Android systems don't behave like a hard drive when connected to a PC but use another shitty "multimedia storage" protocol that seem to be the reason why it's so unstable. Whatever I try, I gotta do it very quickly. I start KingoRoot (the only software that seems genuine in the middle of all the scams), and it tells me to enable the "USB debugging" feature of my phone.

Huh? No USB debugging in the settings.

- Me: "Google, WTF?"
- Google: "USB debugging is in the developper settings ( ͡° ͜ʖ ͡°)".
- Me: "Oh okay, cool... wait, Google, where are the developper settings?"
- Google: "They're hidden, for your safety ( ͡° ͜ʖ ͡°)".
- Google: "Go in System Settings and tap eight times the 'version number' line ( ͡° ͜ʖ ͡°)".
- Me: "(╯°□°)╯︵ ┻━┻".
- Google: "¯\_(ツ)_/¯"

A few minutes later, KingoRoot agrees to start working, and at 70%, the phone restarts. Wait, what? Is that part of the process? Of course not. KingoRoot hangs, the phone reboots half-rooted, which is as useful as no rooted at all.

Second try: plug the phone, launch KingoRoot, wait until 100%, and both KingoRoot and the phone tell me victoriously that the root has been successful... ly... removed. THE FUCK? all it did was removing the previous root attempt.

Third try: Same process, it arrives to 100%, and this time it tells me that it seems to have worked.

I launch the SD unlock app, it asks me the permission to request root access, I say OK, it says "processing...", then "Failed. Your phone may not be rooted.". EAT MY ASS, PHONE!

I install and launch another 25MB (!) app called Root Checker whose only purpose is to tell me if my phone is rooted, and the answer is yes. GODDAMMIT.

I retry the SD unlock app, allow it to ask root access, "processing...", and then it tells me that it's sorry but my XML file has already been successfully edited. GO BURN IN HELL, APP.

I finally made it, after hours of shitty hacking. Hours of my life wasted because of shitty systems, with shitty protections, shitty drivers, shitty protocols and shitty error handling. And it's not the first time something like that happens to me or to someone I know! But apart from tiring the hell out of me, this adventure made me wonder...

How could a non-engineer, or someone who doesn't have an entire day to waste could have resolved this problem? How could my mother could have done one percent of what I did? She doesn't even have a computer! Are non-tech-savvy people doomed to live with more and more buggy products? Will they soon be rejected by the society, where only the best hackers will be able to make modern devices work? Will we reach a point where even the best hackers, or the people who make these systems, won't event be able to use them?

Or will there be a savior, a company that builds better systems than Google, Apple and Windows, systems that just work, without any hack? Free startup idea for you, guys! How about me? I won this battle, but the shit-hack fatigue got me. I'm gonna live in the wood and tame goats. See ya.


The quest of the 🌍🔬🕸🇬🇱🎠 (World's Smallest WebGL Playground)

april to june 2016, january 2017

See the JS1k 2017 update here!
(or continue reading for the full story)

During the last few weeks, the codegolf team (including Mathieu 'p01' Henri, Martin 'Aemkei' Kleppe, Subzey, Anders Kaare, ...) has welcomed new members (LiterallyLara & HellMood, authors of JS1K 2016 winning entry Romanesco 2.0) and gave itself a nice challenge: develop the smallest possible HTML/JS (ES6) boilerplate that's able to run a WebGL fragment shader. It's currently as short as 349b, and looks like this:

<canvas id=a><svg onload='for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)vA(P=cP(setInterval(x=>dr(6,vertexAttrib1f(1,NO++),3),A=s=>sS(S=cS(FN++),id+"varying lowp vec4 p,t;void main(){gl_"+s)|ce(S)|!aS(P,S))),2,5120,bD(B=ET-3,Int8Array.of(A`FragColor=[SHADER]}`,B,!eV(bf(B,cB(id="attribute vec4 P,T;"))),A`Position=p=P;t=T;}`),B+82),!lo(P),ug(P))'>

See detailed source code below!

With this boilerplate, you have access to a time counter t.x (a frame counter), the clip space coordinates c (representing gl_PointCoord), and all you need to do is replace [SHADER] with the value set by your shader to gl_FragColor, for example: p+sin(atan(p.y,p.x)*9.)*sin(p*t.x/40.+9./dot(p,p)+t.x/40.); (DEMO).


- You can set the size of the canvas with things like a.width=a.height=600; or a.width=outerWidth;a.height=outerHeight; at the beinning of the JS code.
- You can change the animation speed by changing the way t is incremented, NO+=.1 (t incremented by 0.1 at each frame) is often a better trade-off than NO++ (t incremented by 1 at each frame).

Here are some gorgeous demos made by LiterallyLara, with this template, in less than 512b.

For code-golfers and demosceners, this boilerplate offers a total of 163b dedicated to the shader in a 512b demo (or 675b in a 1kb demo, not counting the gain of RegPack or gzip): more than never before in the history of WebGL demo'ing!

Making-of & Interactive playground

Between 2014 and 2015, it took us nearly 60 golfing iterations to make the Codepen clone miniCodeEditor fit in 158b. Since then, I talked with Subzey and wondered if we could do something similar with WebGL, maybe a clone of Shadertoy. I tried many times to understand / simplify the source code of shadertoy, but it was too big and too complex (694kb of JS, with 148kb really dedicated to the app's engine.)

So I reversed the problem and decided to start from a finished, milimalist WebGL demo and try to make it editable, like on ShaderToy.

WebGL implementation is very (very) cryptic, but by chance, Lara's post-mortem gave me all the keys to get started.

As a result, we managed to release, and golf MiniShadertoy, including almost all the features of Shadertoy in less than 1kb...

and then MiniShadertoyLite, allowing to fiddle with just a fragment shader in less than 512b.

While we were discussing about it, and how we could golf it even better, Aemkei had the idea to make a "static" boilerplate, basically the same thing as MiniShadertoy, but not editable. Just the strict minimum required to play a hardcoded shader.

You'll find below a sumary of all the golfing steps that led it to its final, almost unreadable form, and a "clean", commented version.
If you're not familiar with WebGL, you can start by reading the commented source code of MiniShadertoyLite HERE.

How we code-golfed it, step by step:

// Step 1: start from MiniShadertoyLite, ignore the textarea, minify, apply RegPack's webgl method hashing, replace all the constants with their numeric value

<body onload='for(i in g=a.getContext(`webgl`))g[i[0]+i[7]+[i[13]]]=g[i];setInterval(`g.uniform1f(g.goa(P,"T"),T++);g.da(6,0,3)`,9);with(g)P=cr(),so(S=ch(35633),`attribute vec2 P;${V="void main(){gl_"}Position=vec4(P,0,1);}`),cS(S),ah(P,S),so(S=ch(35632),`precision lowp float;uniform float T;${V}FragColor=[SHADER]}`),cS(S),ah(P,S),lg(P),ur(P),bf(B=34962,cu()),eet(T=0),vto(0,2,5120,0,0,0),ba(B,new Int8Array([-3,1,1,-3,1,1]),35044)'><canvas id=a>

// Step 2: simplify `gl_Position=vec4(P,0,1)` to `gl_Position.xy=P`

<body onload='for(i in g=a.getContext(`webgl`))g[i[0]+i[7]+[i[13]]]=g[i];setInterval(`g.uniform1f(g.goa(P,"T"),T++);g.da(6,0,3)`,9);with(g)P=cr(),so(S=ch(35633),`attribute vec2 P;${V="void main(){gl_"}Position.xy=P;}`),cS(S),ah(P,S),so(S=ch(35632),`precision lowp float;uniform float T;${V}FragColor=[SHADER]}`),cS(S),ah(P,S),lg(P),ur(P),bf(B=34962,cu()),eet(T=0),vto(0,2,5120,0,0,0),ba(B,new Int8Array([-3,1,1,-3,1,1]),35044)'><canvas id=a>

// Step 3: place the `;` in the template, reuse B at the end

<body onload='for(i in g=a.getContext(`webgl`))g[i[0]+i[7]+[i[13]]]=g[i];setInterval(`g.uniform1f(g.goa(P,"T"),T++);g.da(6,0,3)`,9);with(g)P=cr(),so(S=ch(35633),`attribute vec2 P${V=";void main(){gl_"}Position.xy=P;}`),cS(S),ah(P,S),so(S=ch(35632),`precision lowp float;uniform float T${V}FragColor=[SHADER]}`),cS(S),ah(P,S),lg(P),ur(P),bf(B=34962,cu()),eet(T=0),vto(0,2,5120,0,0,0),ba(B,new Int8Array([-3,1,1,-3,1,1]),B+82)'><canvas id=a>

// Step 4: `with(g)` including the setInterval, arrow function inside setInterval, `int8array.of()`

<body onload='for(i in g=a.getContext(`webgl`))g[i[0]+i[7]+[i[13]]]=g[i];with(g)setInterval(x=>uniform1f(goa(P,"T"),T++)+da(6,0,3),9),P=cr(),so(S=ch(35633),`attribute vec4 P${V=";varying lowp vec4 c;void main(){gl_"}Position=c=P;}`),(A=x=>cS(S)+ah(P,S))(),so(S=ch(35632),`uniform lowp float T${V}FragColor=[SHADER]}`),A(),lg(P),ur(P),bf(B=34962,cu()),eet(T=0),vto(0,2,5120,0,0,0),ba(B,Int8Array.of(-3,1,1,-3,1,1),B+82)'><canvas id=a>

// Step 5: move some reused code inside the `A()` function, and reuse B better

<body onload='for(i in g=a.getContext(`webgl`))g[i[0]+i[7]+[i[13]]]=g[i];with(g)setInterval(x=>uniform1f(goa(P,"T"),T++)+da(6,0,3),9),P=cr(),B=35633,(A=s=>so(S=ch(B--),s)+cS(S)+ah(P,S))(`attribute vec4 P${V=";varying lowp vec4 c;void main(){gl_"}Position=c=P;}`),A(`uniform lowp float T${V}FragColor=[SHADER]}`),lg(P),ur(P),bf(B=34962,cu()),eet(T=0),vto(0,2,5120,0,0,0),ba(B,Int8Array.of(-3,1,1,-3,1,1),B+82)'><canvas id=a>

// Step 6: replace "body onload=..." with "svg onload=..."

<canvas id=a><svg onload='for(i in g=a.getContext(`webgl`))g[i[0]+i[7]+[i[13]]]=g[i];with(g)setInterval(x=>uniform1f(goa(P,"T"),T++)+da(6,0,3),9),P=cr(),B=35633,(A=s=>so(S=ch(B--),s)+cS(S)+ah(P,S))(`attribute vec4 P${V=";varying lowp vec4 c;void main(){gl_"}Position=c=P;}`),A(`uniform lowp float T${V}FragColor=[SHADER]}`),lg(P),ur(P),bf(B=34962,cu()),eet(T=0),vto(0,2,5120,0,0,0),ba(B,Int8Array.of(-3,1,1,-3,1,1),B+82)'>

// Step 7: use a simpler hashing (`g[i[0]+i[6]]` instead of `g[i[0]+i[7]+[i[13]]]`)

<canvas id=a><svg onload='for(i in g=a.getContext(`webgl`))g[i[0]+i[6]]=g[i];with(g)setInterval(x=>uniform1f(gf(P,"T"),T++)+dr(6,0,3),9),P=cP(),B=35633,(A=s=>sS(S=cS(B--),s)+ce(S)+aS(P,S))(`attribute vec4 P${V=";varying lowp vec4 c;void main(){gl_"}Position=c=P;}`),A(`uniform lowp float T${V}FragColor=[SHADER]}`),lo(P),ug(P),bf(B=34962,cB()),eV(T=0),vA(0,2,5120,0,0,0),bD(B,Int8Array.of(-3,1,1,-3,1,1),B+82)'>

// Step 8: replace the first usages of `B` (36532, 36533) with hashed properties (`g.FN` and `g.FN+1`, simplified to `g.FN++` called twice)

<canvas id=a><svg onload='for(i in g=a.getContext(`webgl`))g[i[0]+i[6]]=g[i];with(g)setInterval(x=>uniform1f(gf(P,"T"),T++)+dr(6,0,3),9),P=cP(),(A=s=>sS(S=cS(FN++),s)+ce(S)+aS(P,S))(`uniform lowp float T${V=";varying lowp vec4 c;void main(){gl_"}FragColor=[SHADER]}`),A(`attribute vec4 P${V}Position=c=P;}`),lo(P),ug(P),bf(B=34962,cB()),eV(T=0),vA(0,2,5120,0,0,0),bD(B,Int8Array.of(-3,1,1,-3,1,1),B+82)'>

// Step 9: replace `T` with `g.NO`, which is already set to 0

<canvas id=a><svg onload='for(i in g=a.getContext(`webgl`))g[i[0]+i[6]]=g[i];with(g)setInterval(x=>uniform1f(gf(P,"T"),NO++)+dr(6,0,3),9),P=cP(),(A=s=>sS(S=cS(FN++),s)+ce(S)+aS(P,S))(`uniform lowp float T${V=";varying lowp vec4 c;void main(){gl_"}FragColor=[SHADER]}`),A(`attribute vec4 P${V}Position=c=P;}`),lo(P),ug(P),bf(B=34962,cB()),eV(0),vA(0,2,5120,0,0,0),bD(B,Int8Array.of(-3,1,1,-3,1,1),B+82)'>

// Step 10: simplify the constant `34962` with `g.ET-3`

<canvas id=a><svg onload='for(i in g=a.getContext(`webgl`))g[i[0]+i[6]]=g[i];with(g)setInterval(x=>uniform1f(gf(P,"T"),NO++)+dr(6,0,3),9),P=cP(),(A=s=>sS(S=cS(FN++),s)+ce(S)+aS(P,S))(`uniform lowp float T${V=";varying lowp vec4 c;void main(){gl_"}FragColor=[SHADER]}`),A(`attribute vec4 P${V}Position=c=P;}`),lo(P),ug(P),bf(B=ET-3,cB()),eV(0),vA(0,2,5120,0,0,0),bD(B,Int8Array.of(-3,1,1,-3,1,1),B+82)'>

// Step 11: Subzey joins the game... remove `getContext` parenthesis, replace the second param of `g.dr` (0) with the result of the neighbour function `uniform1f()`. Renamem c into p and T to t

<canvas id=a><svg onload='for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)setInterval(x=>dr(6,uniform1f(gf(P,"t"),NO++),3),P=cP(),(A=s=>sS(S=cS(FN++),s)+ce(S)+aS(P,S))(`uniform lowp float t${V=";varying lowp vec4 p;void main(){gl_"}FragColor=[SHADER]}`),A(`attribute vec4 P${V}Position=p=P;}`),lo(P),ug(P),bf(B=ET-3,cB()),eV(0),vA(0,2,5120,0,0,0),bD(B,Int8Array.of(-3,1,1,-3,1,1),B+82))'>

// Step 12: get rid of some more zeros
<canvas id=a><svg onload='for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)setInterval(x=>dr(6,uniform1f(gf(P,"t"),NO++),3),P=cP(),(A=s=>sS(S=cS(FN++),s)+ce(S)+aS(P,S))(`uniform lowp float t${V=";varying lowp vec4 p;void main(){gl_"}FragColor=[SHADER]}`),vA(A(`attribute vec4 P${V}Position=p=P;}`),2,5120,lo(P),ug(P),eV(bf(B=ET-3,cB()))),bD(B,Int8Array.of(-3,1,1,-3,1,1),B+82))'>

// Step 13: move `A` declaration into `cP()`

<canvas id=a><svg onload='for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)setInterval(x=>dr(6,uniform1f(gf(P,"t"),NO++),3),P=cP(A=s=>sS(S=cS(FN++),s)+ce(S)+aS(P,S)),A(`uniform lowp float t${V=";varying lowp vec4 p;void main(){gl_"}FragColor=[SHADER]}`),vA(A(`attribute vec4 P${V}Position=p=P;}`),2,5120,lo(P),ug(P),eV(bf(B=ET-3,cB()))),bD(B,Int8Array.of(-3,1,1,-3,1,1),B+82))'>

// Step 14: move `vA`, replace the ones in `Int8Array.of()` with neighbor functions

<canvas id=a><svg onload='for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)setInterval(x=>dr(6,uniform1f(gf(P,"t"),NO++),3),!vA(P=cP(A=s=>sS(S=cS(FN++),s)+ce(S)+aS(P,S)),2,5120,A(`uniform lowp float t${V=";varying lowp vec4 p;void main(){gl_"}FragColor=[SHADER]}`),eV(bf(B=ET-3,cB())),bD(B,Int8Array.of(-3,1,!A(`attribute vec4 P${V}Position=p=P;}`),-3,!lo(P),!ug(P)),B+82)))'>

// Step 15: move `vA(P=cP(...))` around the `setInterval`

<canvas id=a><svg onload='for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)vA(P=cP(setInterval(x=>dr(6,uniform1f(gf(P,"t"),NO++),3),A=s=>sS(S=cS(FN++),s)+ce(S)+aS(P,S))),2,5120,A(`uniform lowp float t${V=";varying lowp vec4 p;void main(){gl_"}FragColor=[SHADER]}`),eV(bf(B=ET-3,cB())),bD(B,Int8Array.of(-3,1,!A(`attribute vec4 P${V}Position=p=P;}`),-3,!lo(P),!ug(P)),B+82))'>

// Step 16: change the Int8Array's vertices to `3,-1,-1,3,-1,-1` and use `~` to turn `null` into `-1`

<canvas id=a><svg onload='for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)vA(P=cP(setInterval(x=>dr(6,uniform1f(gf(P,"t"),NO++),3),A=s=>sS(S=cS(FN++),s)+ce(S)+aS(P,S))),2,5120,A(`uniform lowp float t${V=";varying lowp vec4 p;void main(){gl_"}FragColor=[SHADER]}`),eV(bf(B=ET-3,cB())),bD(B,Int8Array.of(3,-1,~A(`attribute vec4 P${V}Position=p=P;}`),3,~lo(P),~ug(P)),B+82))'>

// Step 17: Make `A()` always return `-1`

<canvas id=a><svg onload='for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)vA(P=cP(setInterval(x=>dr(6,uniform1f(gf(P,"t"),NO++),3),A=s=>sS(S=cS(FN++),s)|ce(S)|~aS(P,S))),2,5120,0,eV(bf(B=ET-3,cB())),bD(B,Int8Array.of(3,A(`uniform lowp float t${V=";varying lowp vec4 p;void main(){gl_"}FragColor=[SHADER]}`),A(`attribute vec4 P${V}Position=p=P;}`),3,~lo(P),~ug(P)),B+82))'>

// Step 18: change the shape of the triangle, using the return value of `setInterval` (usually between 1 and 127) as first param of `Int8Array`

<canvas id=a><svg onload='for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)vA(P=cP(),2,5120,bD(B=ET-3,Int8Array.of(B,setInterval(x=>dr(6,uniform1f(gf(P,"t"),NO++),3),A=s=>sS(S=cS(FN++),s)|ce(S)|aS(P,S)),!A(`uniform lowp float t${V=";varying lowp vec4 p;void main(){gl_"}FragColor=[SHADER]}`),B,!eV(bf(B,cB())),!A(`attribute vec4 P${V}Position=p=P;}`)),B+82),lo(P),ug(P))'>

// Step 19: introduce C and T as attributes, set `t.x=T.x`, and use `vA` (vertexAttrib1f) instead of `uniform1f`

<canvas id=a><svg onload='for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)vA(P=cP(),2,5120,bD(B=ET-3,Int8Array.of(B,setInterval(x=>dr(6,vertexAttrib1f(1,NO++),3),A=s=>sS(S=cS(FN++),s)|ce(S)|aS(P,S)),!A(`${V="varying lowp vec4 p,t;void main(){gl_"}FragColor=[SHADER]}`),B,!eV(bf(B,cB())),!A(`attribute vec4 P,T;${V}Position=p=P;t.x=T.x;}`)),B+82),lo(P),ug(P))'>

// Step 20: Set `t=T` directly

<canvas id=a><svg onload='for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)vA(P=cP(),2,5120,bD(B=ET-3,Int8Array.of(B,setInterval(x=>dr(6,vertexAttrib1f(1,NO++),3),A=s=>sS(S=cS(FN++),s)|ce(S)|aS(P,S)),!A(`${V="varying lowp vec4 p,t;void main(){gl_"}FragColor=[SHADER]}`),B,!eV(bf(B,cB())),!A(`attribute vec4 P,T;${V}Position=p=P;t=T;}`)),B+82),lo(P),ug(P))'>

// Step 21: Set 5th parameter of vA (i.e. `vertexAttribPointer`), stride, to 1, to  make the coordinate pairs overlap: `int8Array.of(x1 = 1, y1 = x2 = -3, y2 = x3 = 1, y3 = 1)`. Also, the setTimeout is moved into `cP` (i.e. `createProgram`)

<canvas id=a><svg onload='for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)vA(P=cP(setInterval(x=>dr(6,vertexAttrib1f(1,NO++),3),A=s=>sS(S=cS(FN++),s)|ce(S)|aS(P,S))),2,5120,bD(B=ET-3,Int8Array.of(!A(`${V="varying lowp vec4 p,t;void main(){gl_"}FragColor=[SHADER]}`),B,!eV(bf(B,cB())),!A(`attribute vec4 P,T;${V}Position=p=P;t=T;}`)),B+82),!lo(P),ug(P))'>

// Step 22: Make `A` return 1 in order to avoid calling `!A()` with a `!` twice

<canvas id=a><svg onload='for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)vA(P=cP(setInterval(x=>dr(6,vertexAttrib1f(1,NO++),3),A=s=>sS(S=cS(FN++),s)|ce(S)|!aS(P,S))),2,5120,bD(B=ET-3,Int8Array.of(A(`${V="varying lowp vec4 p,t;void main(){gl_"}FragColor=[SHADER]}`),B,!eV(bf(B,cB())),A(`attribute vec4 P,T;${V}Position=p=P;t=T;}`)),B+82),!lo(P),ug(P))'>

// Step 23: Rearrange `Int8Array.of()` arguments to place `!eV()` first, and move `V` declaration into `cb()`

<canvas id=a><svg onload='for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)vA(P=cP(setInterval(x=>dr(6,vertexAttrib1f(1,NO++),3),A=s=>sS(S=cS(FN++),s)|ce(S)|!aS(P,S))),2,5120,bD(B=ET-3,Int8Array.of(!eV(bf(B,cB(V="varying lowp vec4 p,t;void main(){gl_"))),B,A(V+`FragColor=[SHADER]}`),A(`attribute vec4 P,T;${V}Position=p=P;t=T;}`)),B+82),!lo(P),ug(P))'>

// Step 24: use the svg's id (which is an empty string)

<canvas id=a><svg onload='for(i in g=a.getContext`webgl`)g[i[0]+i[6]]=g[i];with(g)vA(P=cP(setInterval(x=>dr(6,vertexAttrib1f(1,NO++),3),A=s=>sS(S=cS(FN++),id+"varying lowp vec4 p,t;void main(){gl_"+s)|ce(S)|!aS(P,S))),2,5120,bD(B=ET-3,Int8Array.of(A`FragColor=[SHADER]}`,B,!eV(bf(B,cB(id="attribute vec4 P,T;"))),A`Position=p=P;t=T;}`),B+82),!lo(P),ug(P))'>

Mini WebGL boilerplate, commented:

<canvas id=a>

// Canvas methods hashing:
// This loop creates tiny shortcuts for all the webgl context's methods/constants we need:
// g.createProgram => g.cP
// g.shaderSource => g.sS
// g.createShader => g.cS
// g.compileShader => g.ce
// g.attachShader => g.aS
// g.linkProgram => g.lo
// g.useProgram =>
// g.bindBuffer =>
// g.createBuffer => cB
// g.enableVertexAttribArray => g.eV
// g.vertexAttribPointer => g.vA
// g.bufferData => g.bD
// g.getUniformLocation =>
// g.drawArrays => g.dr
// g.NO_ERROR => g.NO (value = 0)
// g.FRAGMENT_SHADER => g.FN (value: 35632)
// g.ELEMENT_ARRAY_BUFFER_BINDING => g.ET (value: 34965)
for(i in g=a.getContext(`webgl`)){

id = "";                                    // The following code is minified and placed in an <svg onload="...">. The variable "id" below is therefore an empty string, until we rename it.

with(g){                                    // Keep g into scope for future function calls (ex: vA == g.vA)
  vA(                                       // Call vA (vertexAttribPointer) with the following params: vertex_index = 0, components_per_vertex_attribute = 2, vertex_attributes_type = g.BYTE = 5120, normalized = 0, stride = 1, offset = 0.
    P=cP(                                   // Call cP (createProgram) to create the program P and return 0 as 1st param of vA
      setInterval(                          // Start the main loop
        x=>dr(6,vertexAttrib1f(1,NO++),3),  // Inside the loop, increase the uniform variable T (by reusing g.NO, initialized at 0) and call g.drawArrays with the params: mode = g.TRIANGLE_FAN = 6, first = 0, count = 3.
        A=s=>{                              // Then define the function A that takes a source string s...
          sS(                               // set the shader's source (sS = shaderSource)...
            S=cS(FN++),                     // with a new shader (cs = createShader), increases g.FN...
            id                              // and use as source the svg's id, plus a constant piece of code, plus s.
            "varying lowp vec4 p,t;void main(){gl_"
          |ce(S)                            // compiles the shader (ce = compileShader)...
          |!aS(P,S)                         // attaches the shader to P (aS = attachShader), and return 1 because Firefox needs a positive integer as the 2nd param of setInterval.
    2,                                      // 2nd param of vA
    5120,                                   // 3rd param of vA
    bD(                                     // Fill bD (bufferData), and return 0 as 4th param of vA
      B=ET-3,                               // Initialize B (1st param of bD) to g.ARRAY_BUFFER = 34962 = g.ET - 3. we can't use g.AB as it's overwritten during hashing, and doesn't correspond to g.ARRAY_BUFFER anymore)
      Int8Array.of(                         // Set the coordinates of a big triangle surrounding the canvas (x1=1, y1=x2=B, y2=x3=1, y3=1) as the 2nd param of bD. The coordinates overlap because the "stride" param of vA is set to 1.
        A`FragColor=[SHADER]}`,             // x1 = A("FragColor=[SHADER]}") = 1. We call A() a first time to define the fragment shader (at this moment, g.FN = g.FRAGMENT_SHADER = 36532)
        B,                                  // y1 = x2 = B = 34962
        !eV(                                // y2 = x3 = !eV(0) = !enableVertexAttribArray(0) = 1
          bf(                               // Use bf (bindBuffer) to bind the buffer created on-the-fly with cB and return 0
            B,                              // g.ARRAY_BUFFER
            cB(                             // g.createBuffer
              id="attribute vec4 P,T;"      // set the id of the svg, as a prefix for the next time we call A()
        A`Position=p=P;t=T;}`               // y3 = A(Position=p=P;t=T;}) = 1. We call A() a second time to create the vertex shader (at this moment, g.FN = 35633 = g.VERTEX_SHADER)
      B+82                                  // 3rd param of bD: g.STATIC_DRAW = 35044 = 34962 + 82
    !lo(P),                                 // Link the program P (lo = linkProgram), return 1 as 5th param of vA
    ug(P)                                   // Use the program P (ug = useProgram), return 0 as 6th param of vA


With all this progress, MiniShadertoyLite became a little outdated... so we made a new version including all the previous improvements.
There's now enough space to include a fullscreen canvas and a contenteditable body and remain under 512b!
Here's the last version of MiniShadertoyLite:

And thanks to @innovati for the Emoji title: 🌍🔬🕸🇬🇱🎠 !

01/2017 update

We made an entry for JS1k 2017 featuring all the tricks of this article, plus a nice UI and great compatibility with!

You ca find the entry HERE.

The commented source code is HERE.


The Codegolf team

JS1k 2016

febuary - march 2016

JS1k is certainly code-golfing event that makes me wait for febuary to come, during all the rest of the year.

This year, the theme was EleMental, and I decided to make two chemistry-related demos with the help of my friends Innovati and Subzey (a.k.a. The Codegolf Team), plus a couple of entries by myself.

If you're curious about how they were made or how they can fit in 1kb, take a look at the detailed source code. It's very detailed.

If you want to support us for the "social prize", you can upvote the demos on reddit!


Our first challenge was to draw a pretty periodic table of chemical elements with as much information as possible. After long efforts of data compression dead-ends and successes, we released PERIOD1k. It was the first entry of the compo.

- The entry on js1k (1024b)
- The post on reddit
- The project (with bonuses) on Github
- The detailed source code


We then gave ourselves another compression challenge: draw a complete table of isotopes (or table of nuclides). This time, there was so much data to include th the app that the JS code used to draw the chart only takes ~10% of the demo, all the rest is used for the position, number and decay types of all the known isotopes.

- The entry on js1k (1023b)
- The post on reddit
- The project (with bonuses) on Github
- The detailed source code

PS: In febuary, researchers discovered the tetraneutron particle. I didn't manage to include it in our demo in less than 1025b. ARRGGH.


For my first individual entry, I wanted to revisit a Unicode slideshow that I made a few months earlier, called MiniUnicode. It's actually a collection of slideshows made to fit in 64b, 128b, 256b, 512b, 4kb, 512kb, including more and more details at each version. The 512b version only shows the assigned code points, but the 4kb version includes all the Unicode blocks names. I decided to do something in the middle: a demo that shows all the assigned code points, but is also aware of all the blocks sizes, and able to start the animation from any of these blocks. That required a lot of compression and transformation of the original code, but it worked!

- The entry on js1k (1024b)
- The post on reddit
- The project on Github
- The detailed source code


This final challenge came as a surprise, while I was developping my code-golfing IDE. Subzey suggested that I add a code beautifier to see the "unminified" version of our code in real time, so I decided to develop one from scratch. Subzey also told me it was impossible to achieve this task using RegExes, so I took it as a personal challenge, and succeeded. Then I golfed my code a little and realized it could fit in 1kb with some extra compression. And that's what I did.

- The entry on js1k (1022b)
- The post on reddit
- The project on Github
- The detailed source code

Bonus: emulation!

There are some other 1kb projects that I started to prepare before febuary, but they weren't good enough (or small enough) to be submitted. Here they are in exclusivity, just for you:

Here's a Chip8 ROM decompiler (1024b gzipped)

And a Chip8 emulator with sound, packed in a js1k shim (~1040b regpacked, but still buggy)

(Both are inspired by my old Chip8 emulator project, where I had made a PNG-bootstrapped Chip8 emulator, without sound, in 1028b).

Bonus 2: Compilations!

I also prepared two compilations including all the tiny apps and games that I golfed with the team
(@p01, @subzey, @aemkei, @0ndras, @maettig and @ilesinge) during the past year.
Sadly, none of them fits in 1kb but you can try them here!

4 Games (1230b)

(Featuring MiniGameOfBraille, MiniFlappyBraille, Ping & Pacman)

7 Apps (1157b)

(Featuring MiniMandelbrot, MiniMandelbrot ASCII, MiniBookmarklet, Countdown 2017, Xmas trees, MiniKeyCode & MiniUnicode)


Obfusc-a-tweet reloaded

november-december 2015

TL;DR - the app is here:

Last year, Subzey and I introduced Obfusc-a-tweet, allowing to fit 190 chars of JavaScript in a single 140-chars tweet, by packing a pair of ASCII chars in 95 Unicode symbols and using the other 45 chars to unpack and execute them.

This time, the idea is to take advantage of Twitter's URL shortener!

When you tweet an URL, Twitter currently shrinks it (or lenghtens it) into a 23-chars URL, in the form

In the final tweet, your original URL can appear truncated on the left or on the right, or both, according to this rule.

But when you copy-paste such a tweet from Twitter to a text editor or a JS console, you get the entire original URL (instead of the shortened URL or the truncated URL), followed by a space and an "…" ellipsis character.

Testing the limits

I made some experiments so find the limits of this system and here's what I found:

Packing JS code

So, if we want to store a lot of text in a 140-chars tweet, the theorical limit is about 5 x 4084 url-encoded chars (stored into 5 URLs) + 4 spaces or punctuations between each URL + 21 other characters.

But our goal is to have an executable tweet, that can be copied as-is into a JS console and run instantly. Thus, the tweet needs to contain some long URLs plus a small JS unpacker and executer. After a lot of trial and error, here is the most efficient solution I could find: (it contains only 4 URLs and uses all the other chars to unpack and execute the JS code).

eval(decodeURI(" 🖝".replace(/ .{1,14}/g,'')))

... where WWW, XXX, YYY and ZZZ are url-encoded strings up to 4084 chars long.

Note that the four URLs are placed into a long string, surrounded by JS code.

When this message is tweeted, it takes exactly 140 characters.

When it's copy-pasted from Twitter to a JS console, the result is almost the same thing, the only difference being the spaces and ellipsis added after each URL:

eval(decodeURI(" 🖝 … … … …".replace(/ .{1,14}/g,'')))

When you execute that into a JS console, here's what happens:

// step 0: copy-paste the tweet

eval(decodeURI(" 🖝 … … … …".replace(/ .{1,14}/g,'')))

// step 1: the replace(/ .{1,14}/g,'') transforms the string to keep only the interesting parts
// it removes the beginning: " 🖝", because it's a space followed by 14 characters.
// The "🖝" is an astral Unicode character. It counts as two chars in JS, but only one on Twitter.
// It also removes the three occurences of " …" (space + 14 chars)
// And it also removes the end: " …" (space + 1 char).
// After the replace, here's the remaining string:


// step 2: the remaining string is url-decoded to produce the final code
// for example, any occurrence of "%7D" will be decoded to a tilde ("~"), etc.

eval("final code")

// step 3: the remaining string (our final JS code) is evaluated

The 🖝 symbol can be replaced by any astral Unicode symbol (U+10000 - U+10FFFF).

At this point, we can potentially store up to 4 * 4084 = 16336 chars in a single tweet! But this total includes escape sequences for many characters that can't be used into URLs (such sequences take 3 to 12 chars each).

A word about encoded URIs

Here are the 95 printable ASCII chars:


Here are the same chars after passing them through JS's encodeURI:


Basically, according to JS, the only chars that need to be escaped are space, ", %, <, >, [, \, ], ^, `, {, | and }.

But Twitter has some quirks: it doesn't support URLs containing empty parenthesis like "()" or unbalanced parenthesis like "a(a)a)" or "(a(a)a" and on the other hand, it supports URLs containing %, [, ] and |.

We need to take that into account to build a packer as effective as possible. (We will keep parenthesis and percent signs escaped to avoid weird parsing bugs, and we will keep [, ] and | unescaped because they are safe enough to be used like that in our URLs).

Building the packer

To sum things up, if we want to make a JS packer, the mission is to take a JS string as input, URL-encode it, URL-encode the parenthesis characters that can cause problems on Twitter, decode the three chars that don't need to be encoded ([, ], |), split the result in 4084-char blocks, pack these blocks in URLs, surround these URLs with an unpacker/executer, and output the result as a tweetable message.

Here's the packer! (use the first option to test the above technique)

And here's a demo tweet that alerts a 12641-char text

UPDATE #1: Go further with charcode shifting!

After releasing the first version of this packer, I realized with Anders Kaare that a lot of non-ASCII chars were actually useable in Twitter's URLs. After some manual research, we discovered that there's actually a total of 483 chars that can be used safely.


This represents a subset of the Unicode blocks "Basic Latin" and "Latin-1 supplement" plus the whole blocks "Latin-extended-A" and "Latin-extended-B", containing respectively 128 and 256 characters.

So I had the idea to "shift" all the ASCII characters present in the JS source code (U+0000 - U+007F), to represent them with Latin-extended-A characters (U+0100 - U+017F). The advantage here is to avoid all the escape sequences needed to represent the special chars of our JS code in an URL. All we need to do is to add 0x100 to each charcode.

With the previous version, a 14-chars string like "Hello World ^^" was URL-encoded as "Hello%20World%20%5E%5E" (which is 22 chars long). With this "shift", it's converted to "ňťŬŬůĠŗůŲŬŤĠŞŞ" (which is only 14 chars long).

Of course, this shifted code is not directly executable, it needs to be unshifted first. Here's the unshifter I wrote with the help of Subzey:

eval(unescape(escape('...shifted code...').replace(/u../g,'')))

If you're confused by this code, you can read an explanation here. (we used the same principle in the original obfusc-a-tweet app)

We don't have room in the tweet itself to include this unshifter, because the tweet is already filled with the URLs and their unpacker and executer, so we need to place this code directly inside our URLs.

Here's an example of a complete tweet:

eval(decodeURI(" 🖝'WWW … … …'%29.replace(/u../g,'')%29%29 …".replace(/ .{1,14}/g,'')))

where WWW, XXX, YYY and ZZZ respectively represent shifted code up to 4056, 4084, 4084 and 4055 chars long.

Note that the first URL starts with an URL-encoded version of eval(unescape(escape(' and the last URL ends with an URL-encoded version of ').replace(/u../g,''))).

Here's what happens when this tweet is executed:

// step 0: copy-paste the tweet

eval(decodeURI(" 🖝'WWW … … …'%29.replace(/u../g,'')%29%29 …".replace(/ .{1,14}/g,'')))

// Unpacker-side:

// step 1: the replace() transforms the string to keep only the interesting parts
// After the replace, here's the remaining string:


// step 2: the remaining string is url-decoded:


// step 3: the remaining string is evaluated:


// Unshifter-side:

// Step 4: the string is unshifted to produce the final code

eval('final code')

// Step 5: the remaining string is evaluated.

This unshifter consumes 57 chars of our URLs, but it removes all risk of escape sequences. So we're now sure to store up to 16,279 chars of JS in a tweet.

Here's the updated packer! (choose the second option under the input section to use the shifter)

And here's a demo tweet that alerts a 16271-char text

UPDATE #2: Go even further with shift + PNG bootstrapping

So, if we sum up our possibilities right now, we can either execute up to 16,336 chars of JS (including the escape sequences for all the chars that are not URL-safe), or execute up to 16,279 chars of JS (shifted to avoid using any escape sequence). This works fine with minified and even RegPacked code. But what if we could gzip our JS code, embed it into our 16,279 shifted chars, and add a little bit of extra code to extract and execute it?

Well, some people already kinda had this idea, and it's called JsExe. JsExe encodes the chars of any JS code in the pixels of a greyscale PNG image. The PNG format is natively gzipped. The PNG is then saved as a HTML page that includes itself in an IMG, draws it on a canvas, and reads the pixels to retrieve and execute the original JS code. Even though the extre code takes about 200 bytes, this "PNG bootstrapping" technique is generally more efficient than RegPack, even for demos as small as 1kb.

So we decided to try that. First, Subzey and I developed zpng, a pure JS clone of JsExe. It turned out to be even more efficient than JsExe thanks to the use of zopfli compression!

Then I took the code that generates PNG from zpng and included it in obfuscatweet-reloaded.

I made it encode the JS source code into a PNG, converted the PNG bytes into extended ASCII, shifted them, and it gave me something like that:

// Source code
alert("Hello World ^^");

// PNG as dataURI
// PNG's content, as text


// PNG's content, as shifted text

Cool, so now the PNG's content is tweetable. In order to use it, we have to unshift it, convert it in dataURI, put it in an image, draw that image on a canvas, read the pixels of the canvas, transform it in JS and execute the JS.

The principle is the following:

var png = "ƉŐŎŇčĊĚĊĀĀĀčʼnňńŒĀĀĀęĀĀĀāĈĀĀĀĀĎćŚʼnĀĀĀĢʼnńŁŔŸāţňnjʼnĭĪǑŐDzňǍljljŗĈǏįNJʼnőƈƋœǒƴŦĀĀŰǣćǡ";
var g;
// (snip) unshift png and put the result in g
z=new Image;
for($=_=''; C.drawImage(z,$--,0), X=(q=C.getImageData(0,0,64,32)).data[0]; _+=String.fromCharCode(X)){};

Let's golf it:

(z=new Image).src='data:;base64,'+btoa(g);C=document.createElement('canvas').getContext('2d');for($=_='';C.drawImage(z,$--,0),X=(q=C.getImageData(0,0,64,32)).data[0];_+=String.fromCharCode(X));(e=eval)(_)

204 bytes. Great! Now here's the real trick. The PNG content is stored as shifted text. This bootstrap code should also be shifted, to use as few chars as possible. So the idea is to put the two shifted blocks in the same string:

var tmp = 'ĨźĽŮťŷĠʼnŭšŧťĩĮųŲţĽħŤšŴšĺĻŢšųťĶĴĬħīŢŴůšĨŧĮųŬũţťĨIJıĵĩĩĻŃĽŤůţŵŭťŮŴĮţŲťšŴťŅŬťŭťŮŴĨħţšŮŶšųħĩĮŧťŴŃůŮŴťŸŴĨħIJŤħĩĻŦůŲĨĤĽşĽħħĻŃĮŤŲšŷʼnŭšŧťĨźĬĤĭĭĬİĩĬŘĽĨűĽŃĮŧťŴʼnŭšŧťńšŴšĨİĬİĬĶĴĬijIJĩĩĮŤšŴšśİŝĻşīĽœŴŲũŮŧĮŦŲůŭŃŨšŲŃůŤťĨŘĩĩĻĨťĽťŶšŬĩĨşĩ' /*bootstrap*/ + 'ƉŐŎŇčĊĚĊĀĀĀčʼnňńŒĀĀĀęĀĀĀāĈĀĀĀĀĎćŚʼnĀĀĀĢʼnńŁŔŸāţňnjʼnĭĪǑŐDzňǍljljŗĈǏįNJʼnőƈƋœǒƴŦĀĀŰǣćǡ' /* PNG */

Then unshift this string, as we already did in the previous chapter, an put the result in g:


Then we execute only the first 204 chars of g.


Let's go back to the bootstrap code. Instead of creating a dataURI from g, it needs to create it from g.slice(204).

(z=new Image).src='data:;base64,'+btoa(g.slice(204));C=document.createElement('canvas').getContext('2d');for($=_='';C.drawImage(z,$--,0),X=(q=C.getImageData(0,0,64,32)).data[0];_+=String.fromCharCode(X));(e=eval)(_)
But the ".slice(204)" adds 11 chars to the bootstrap, so we need to replace 204 to 215 everywhere:
// In bootstrap
(z=new Image).src='data:;base64,'+btoa(g.slice(215));C=document.createElement('canvas').getContext('2d');for($=_='';C.drawImage(z,$--,0),X=(q=C.getImageData(0,0,64,32)).data[0];_+=String.fromCharCode(X));(e=eval)(_)

// In unshifter

And we're done! Phew! So, here's what happens when we tweet a PNG bootstrapped code:

// step 0: copy-paste the tweet
// WWW, XXX, YYY and ZZZ represent the shifted string containing the bootstrap code and the PNG
eval(decodeURI(" 🖝'WWW … … …'%29.replace(/u../g,'')%29%29.slice(0,215)%29 …".replace(/ .{1,14}/g,'')))

// Unpacker-side:

// step 1: the replace() transforms the string to keep only the interesting parts
// After the replace, here's the remaining string:


// step 2: the remaining string is url-decoded:


// step 3: the remaining string is evaluated:


// Unshifter-side:

// Step 4: unshift, g contains all the unshifted string, and the first 215 chars are evaluated

g = "(z=new Image).src='data:;base64,'+btoa(g.slice(215));C=document.createElement('canvas').getContext('2d');for($=_='';C.drawImage(z,$--,0),X=(q=C.getImageData(0,0,64,32)).data[0];_+=String.fromCharCode(X));(e=eval)(_) // +  PNG content";


// PNG bootstrap side

// z contains an image whose src is g.slice(215)
// C contains a canvas context2d
// _ contains our final JS code
// e is a shortcut for eval

// Step 5: the final code is evaluated.

We successfully embedded a PNG bootstrap technique in a tweet. Now let's break some record! (The last record was 16,279b of JS executed.)

Here's the updated packer! (choose the third option under the input section to use the PNG bootstrap mode)

And here's a demo tweet that logs 37,867 chars of Moby Dick in the browser's console!

Unfortunately, this kind of code currently doesn't work on MS Edge, nor Firefox.

If you insist a little, Firefox agrees to execute up to 16kb of JS packed into a PNG, but not more.

UPDATE #3: ...PAUSE...

We have many more ideas, based on different image compressions and base-483 radix conversion, but Twitter recently announced they will extend the tweets limit to 10,000 characters. Let's wait for that "revolution" before going further!

To be continued!


JS13kGames 2015 post-mortem: GeoQuiz

august-september 2015


Play enhanced version on Github
source code
Reddit discussion

13kb seems small, but actually it's huge... even for a full-featured HTML5 game.
(Last year, despite my efforts, I didn't manage to use more than 9kb).
But rules are rules, and this year again, we have a month to fill a 13kb zip with a high-quality game for... !

To avoid being annoyed by such a high limit, I decided to make something big, something so big that people will ask themselves how it can fit in just 13kb.
Something big enough that I would also wonder how I will pack it in 13kb... and develop my own tools to achieve that goal.
Then yes, JS13kGames would be a great code-golfing and compression challenge!

Games contain code and data. 13kb of golfed code is very hard to write, so I decided to make a game that uses a lot of data.
Hey... how about putting the whole world in my game?
There was this Flash game that I liked a lot at university: Traveller's IQ challenge.
Its goal was to find famous places (cities, capitols, monuments...) on a World map.
I chose to make a game like that, but with my own style.

A few days before the compo...

The final game doesn't contain any code or data produced before the beginning of the compo. During this couple of days, I just began to develop my design tools and produced dummy data that would be replaced afterwards.

As I was waiting for the compo to start, I started experimenting with different techniques to draw and store efficiently all the countries, capitols and famous places of the world. I chose a good-looking, yet outdated World map (I did not want a Mercator projection - It's too stretched and unrealistic around the poles) and hacked a little tool that would let me draw the outline of each country with my mouse, on a canvas. The logic was a bit similar to what I did for Flappy Dragon during JS1k 2014, but with less precision, because this time, I had to draw more than 197 countries. After many experiments, I chose the following way: I placed the model of my map on a 1024 by 512px canvas. Then, for each country and for each point I clicked, the tool would gather two mouse coordinates on the map (X and Y), and append the value of X/4 and Y/2, converted in Unicode, to a string representing the given country. X/4 and Y/2 turned out to be bounded between 0 and 253, so I could use two chars to represent each point, and the character 255 ("ÿ") as a separator for the countries (like Canada) consisting of multiple islands or territories. The capitol's coordinates are appended at the end. With this technique, I could fill a JSON file that looked like this:

    'Brazil,Brasilia': '?É?½5¢0¦/™8‡;AŠB‘NšJ»D¿BÎÿD²',
    'Bulgaria,Sofia': '{?€AD|Eÿ|B',

    'Canada,Ottawa': '8"9<\nFOB\nBGA!<(...);A9D3:7?=;A9;.D+7ÿ%)\r11ÿ+\n/4/\rÿ5:4ÿ8<9\r5ÿ3=',
    //   ⬆                      ⬆                           ⬆      ⬆        ⬆           ⬆⬆
    // Country, capitol    Sequence of X/Y coordinates      Islands separators    Capitol coordinates

    'Chile,Santiago': '7ë4È6¹2±4Ý7ï=õ;ï7ëÿ4Ì',
    'China,Beijing': '©7¢I¨W°^·\\ºj¾gÂkÈdÉXÄHËBË8Ã2ÄB¼MÿÁmÃlÂpÁoÿÂI',

You can try the editor here. (use left click to place a point, right click to start a new island and spacebar to start a new country. The coordinates are appended to the text under the map)

Then, I tried to draw the map from that JSON, and it wasn't as easy and straightforward as I imagined... here are a few screenshots of my first tests:

fig. 1: Unicode madness, most countries leak everywhere and half of Russia somehow gets drawn over south America.

fig. 2: Debugging, displaying small dots for the capitols, redrawing countries that had corrupted data...

fig. 3: More debugging... and realizing that many territories like Greenland and Western Sahara are not countries... Damn, Earth! :)

These tests confirmed that this project was possible. Indeed, the names, shapes and capitols of all the countries on Earth can fit in just 6.2kb in JSON. Now I'm sure there is plenty of room left for a more detailed map, plus the game's code, the music and even more data!

But I didn't stop there. I used the unused char U+254 ("þ") as a separator, and made a big string with all my JSON's data...

Algeria,AlgiersþtfpRqLiOjVc]noÿmNþArgentina,Buenos Airesþ6¸?Ã>Ï@Ö<Ù;ï7ë4Èÿ<ñ=õ@õÿ>Ðþ ... 

Then I made a tool that converts this string in a binary file. After gzipping, I saw my data fell from 6.2kb to 5.6kb! 600 bytes saved, that's a great news! If you're wondering why this conversion is so efficient, it's for two main reasons. First, the JSON contained some escape sequences ("\\" instead of "\", "\r" and "\n" instead of line breaks, \' instead of quotes) that became a single byte in binary (the escape was no longer necessary). But most of the savings come from the "code point-to-binary" conversion for all the characters between U+80 and U+FF (these characters take 2 bytes each in UTF-8, as opposed to just 1 byte if we store their code points in binary). You can find more info about how many bytes are used for each character in each charset on this page.

Here's a golfed encoder that does this conversion in about 230b:

// Unicode to binary encoder
// Input (m) can contain chars between U+0 and U+255
// Output (download) is a binary file
m=m.replace(/\\\\/g,"\\").replace(/\\r/g,"\r").replace(/\\n/g,"\n").replace(/\\'/g,"'");y=[];for(i in m)y.push(m.charCodeAt(i));location="data:application/octet-stream;base64,"+btoa(String.fromCharCode.apply(!1,new Uint8Array(y)))

Of course, at some point, this binary data needs to be read and converted back as a long Unicode string. I made a little JS program that does it in about 160bytes. It's totally worth the overhead, considered the hundreds of bytes that can be saved through binary.

// Binary to Unicode decoder
// After execution, h contains the decoded string.
with(new XMLHttpRequest)open("GET","data.bin"),responseType='arraybuffer',send(),onload=function(){h="";for(i in u=new Uint8Array(response))h+=String.fromCharCode(u[i])}

Golf tip: instead of data.bin, you can name your binary file "0" (or any number), without extension. It will reduce the name used in open() but also make the quotes unnecessary:

with(new XMLHttpRequest)open("GET",0), ... 

Compo: Day 1

When the compo started, my map editor was functional, and I could start developing my game for real. But before that, I wanted to experiment another thing: to represent the Earth on a 3D-looking sphere instead of always a flat map.

I didn't want to use (or make) a 3D engine just for that. I was looking for a very lightweight solution to give the illusion of a real sphere using a few tricks of trigonometry-fu, like Aemkei did for his 1kb world. I'm not an expert but, by chance, my friend Subzey helped me write these simple equations that turn instantly some map coordinates into a projected globe's coordinates:

// map_x and map_y placed in the interval [-1:1]
globe_x = Math.sin(map_x * Math.PI) * Math.cos(map_y * Math.PI / 2);
globe_y = Math.sin(map_y * Math.PI/2);

For more info, tke a look at this demo by Subzey.

Here's what I saw when I tried to adapt this technique to my map.

After debugging, here's a demo using my world map (animated)

After long hours fighting with Canada and Russia (because they are too large to behave correctly where the "-1" and the "1" are connected), I managed to hide the back side of the globe and thus, make it much more realistic.

Here's a demo with only the front face of the globe (animated)

Great! Everything works. All that's left to do now is to develop the game in a few kilobytes, use another couple of kb for music, and all the rest of my zip will be used to store as many data as possible.

The first thing I developed in the game was a "Home screen" (featuring the rotating 3D globe in more realistic colors, plus slightly blinking stars scrolling in the background), and a presentation screen for each "level" of the game.

Compo: Day 2

I improved the graphics with better colors and gradients.

I also began to implement a basic interactivity: showing the name of a target and a 10-second timer, clicking on the map, telling if the target has been clicked, and restarting with the next puzzle.

To find if a country is clicked, I use the canvas function "ctx.isPointInPath(mouseX, mouseY)" just after drawing the country in yellow. It's very handy! But when we click outside of the country, I have to loop on all the points of the country, compute which one is the closest (computing distances using Pythagore), and use it for the green flag and the red line.

Days 3 & 4

During this weekend, I started refactoring some code and compressed my data a little bit more (about 200b) by removing some separator bytes in my binary file. (But *spoiler alert* there will be a big data overhaul in a few days, so I'll talk more about it later!).

I also worked on the gameplay and added an distance counter that tells you an idea of how far you are from a target. This number will be used to compute the player's score.

Of course, Pythagore isn't a great solution to compute distances on a sphere projected on a map, it works well enough for a mini-game.

I also worked on the gameplay of the level 2 (finding capitols):

... and worked on the scoring system (your "error gauge" decreases after every mistake and if it reaches 0, the game is over).

Also, with a little help from my friends and my Twitter friends, I produced an important chunk of data: the names and positions of 100 famous places on Earth, separated in 3 categories (easy, medium, hard). The data looks like this in JSON, and of course, it will be part of the final binary file:

  "Christ the Redeemer": "Hº",
  "The Great Chinese Wall": "ÁI",
  "The Great Sphinx": "ƒZ",
  "The Eiffel Tower", "m9"

Days 5-11

I knew I had too much free space, so I spent the spare time I had this week adding the U.S.A. states and capitols to my database. Same format as the World's cities and capitols, but I directly drew the "final map", i.e. with all the little details I could draw. This brought my total data counter to 7.9kb zipped.

I also decided to reorganize and optimize my data once again, to make it as small and compressible as possible:

JS1k 2015 post-mortem

february 2015

This month was very busy! I spent almost all my free time working golfing for JS1k.

➾ I contributed to the great invitro made by @subzey, by finding a brand new compression technique, allowing to hash the canvas properties as well as the canvas functions.
You can read more about it in his post mortem and the discussion in this RegPack ticket.
It was very exciting to discover this new technique and save about 50 bytes on a demo we thought we couldn't compress anymore!

➾ I gathered the best code-golfed games and apps made during the past months by "the codegolf team" into three compilations:

- Mini Apps Collection
Featuring 7 apps! Mini Code Editor, HTML & CSS minifier, hex viewer, dataURI converter, spritesheet and JSPerf.
(1477b minified, 1024b regpacked)

- Mini Games Collection #1
Mine Sweeper, Pop Tiles, and Game of Life.
(1221b minified, 1023b regpacked)

- Mini Games Collection #2
Simon with sound and 2048.
(1220b minified, 1022b regpacked)

It took a lot of time to fit each of these compilations in 1k, which involved removing some features not compatible with js1k's rules (like accessing "top" and localStorage"), managing to inject big chunks of HTML and JS into the shim with many "document.write", "innerHTML" and "outerHTML"-based hacks, and optimizing the whole code for a better RegPack compression (by repeating code as much as possible, sometimes in very silly ways)...

Thanks to @p01, @subzey, @aemkei, @LauckAndLoad, @bburky, @mathias and @veubeke for their contribution in each of these projects!
You can read more about these apps and games (and their original projects) by clicking the "demo details" button on each demo.

➾The theme was "Hype Train", so I couldn't resist making this little hommage to asdfmovie's sequence I like trains. The code has nothing special, just a lot of points coordinates and a loop displaying them with moveTo and lineTo's. This kind of "monotonous" source code packs very well (1472b minified - 902b regpacked)

➾ The biggest challenge was of course my "serious" entry, hypeRcube!

With all the success of the 3D demos during the past years, my idea was to produce the first 4D demo of JS1k, featuring an "hypercube" (the 4D equivalent of a 3D cube). I had no precise idea of what I would (or could) do in 1k, but I aimed for some kind of animated / rotating hypercube, like the ones we can see on wikipedia's gifs.

It took more than 10 iterations (10 more or less big rewrites) to become what it is today, and each of these rewrites was marked by a new exciting feature, a compression breakthrough, or a funny bug that made me understand that I was not on the good path. You can tke a look at these beta versions here:

Proof of concept, very laggy but featuring an intro, perspective, camera rotation and the 6 4D rotations (3.1kb minified, and only 1.3kb regpacked, due to heavy code reuse). This PoC convinced me that doing a 4D demo for js1k was possible! (tweet)

Beta #1, with more reactive buttons, and intensive golfing (2.0kb minified, 1.1kb regpacked)

Beta #2, almost fitting in 1k (1.9kb minified, 1037b regpacked)

Beta #3, golfed intensively (1.6kb minified, 963b regpacked). From this point I knew that I would be able to add many more features and graphical enhancements in my 1kb demo!
Actually, I added features and golfed them at the same time to stay as close as possible to 1kb.

Beta #4, adding colors, and allowing to play/pause animations... but it's so slow ! (1.5B minified, 1022b regpacked) - funny... bugs.

Beta #5, enhancing speed and adding a perspective slider going from an isometric view (2.5D) to an exaggerated 3D view... (1.5kb minified, 1030b regpacked)

Beta #6, adding two color pickers and optimizing the workflow for RegPack (reusing the vars produced by RegPack and putting all the code in a string that will execute itself repeatedly using a timeout (1.58kb minified, 1042b regpacked) - the main achievement here was to achieve drawing the hypercube in a single pass, using only connected edges and not passing more than one time by the same edge, thanks to this wonderful crazy path I've drawn on paint (other funny bug)

Beta #7, YAY EPILEPTIC PARTY! very buggy but I find it artistic. (see gif)

Beta #8, stabilized and golfed a bit more (1.56kb minified, 974b regpacked)

Beta #9, I tried to fill the hypercube in red but it was a bad idea. (related tweet)

Beta #10 finished, with an extra feature to slow down or speed up the animation (1.57kb minified, 1021b regpacked)

Making this demo helped me understand better the 4th dimension, and even if I only scratched the surface, I find it amazing. I was surprised to see so many features and graphic effects fit in 1kb, thanks to many epic golfing tricks, but also hand-made line-drawing (see demo #6), as well as dirty hacks to optimize the code for RegPack (repeating big chunks as much as possible to reduce entropy), and faking the perspective effect by just drawing lines on a canvas and making the points closer to each other to give the impression that they are further from the camera...

To achieve so many things, the secret holds in two words: passion and organization. If you want to see how organized it is, you can read the source code below. I detailed it as much as I could to make it useful for anyone wanting to discover the tricks used inside.

Thanks for your attention and see you next year!

Source code

Commented (5735b)

Minified (1550b)

Packed (1024b)

In case you were wondering, yeah, I used almost all the alphabet as variable names. This demo contains a big state machine needing all those variables. And yes, it's a heart at the end of the RegPacked code, because I had 3 bytes left and "eval(_) <3" is totally valid. It's useless but it's my way to say that the demo was made with love! :D


PS: please retweet if you liked it!

JS13k 2014 post-mortem

september 2014

JS13kgames is an annual code golfing competition where you can build (desktop / mobile / server-based) HTML5 games that have to fin in a 13kb zip.

13kb is like, infinity for extreme code golfers, but we decided to give it a try anyway:

I made AlcheMixer by myself, you can try it here

I also made Quintessence with @subzey and @nderscore, you can try it here and read @subzey's great notes here!

Sadly, we didn't have time, and didn't even need, to golf the source code of those games, as they were way under the 13kb mark at the end of the month... But it was fun anyway!

Hope you like them! For the record, AlcheMixer was ranked 72th in the desktop games category and Quintessence 8th in the mobile games category.

Cheers, @MaximeEuziere

DOM oddities

july-october 2014

This article will cover a few oddities I found by writing bizarre HTML and inspecting the DOM. I'll update it when I find new things:


The HEAD element is hidden by default on all Web pages.
It can contain elements such as META, TITLE, LINK, STYLE, SCRIPT and BASE.
But what happens when you force them to be visible, and editable? Here's the answer: Show !

As you can see, all those elements can behave like regular DIVs. Even the META, LINK and BASE can become containers, even if they are generally used as self-contained elements (without children nor closing tag). Some browsers even allow to edit the TITLE block and update the browser's titlebar accordingly. Also, the LINK element is shown as an Anchor even though it's not really clickable.


• A paragraph can't contain another paragraph. If you try to do something like <p><p></p></p>, not two but three neighbours <P>s are added in the DOM: the outer one, the inner one, and an empty one caused by the final </p>. Anyway, they won't be nested:

Same thing if you change their display to "inline", they can't be nested at all:

... Unless you write the inner paragraph with JS:

Anchors (<A>) behave like paragraphs in the sense that they can't be nested:

But something strange happens when you try to nest an anchor in a paragraph in an anchor:

That's right, a third empty A is added into the DOM! Go figure...


The HTML, HEAD and BODY tags are optional in the HTML source, but are automatically added in the DOM by the browsers. Did you know they can be removed too?

There's another thing that browsers do automatically: they place some HTML tags in the HEAD and other tags in the BODY, as long as they are in the right order:

But if your HTML source contains HEAD-related elements after BODY-related elements, they'll be in the body (but they'll stay hidden, of course):

To be continued...

JS1k 2014 post-mortem: 2P Games

march 2014

Welcome again, to the making-of of 2P Games classic and 2P Games ++, my team projects for JS1k 2014.

I've made these games with the awesome code golfer Subzey, who helped me in many code-golfing projects last year
(a JSfiddle clone, a chip8 emulator, a Pastebin clone, a JS packer, a JS-to-unicode converter, ...)

Source code

Commented (5735b)

Minified (1613b)

Packed (1024b)

The idea

Subzey and I code-golfed a minesweeper game in 480b in 2013 and it was really fun and very instructive.

So, for JS1k 2014, I gave ourselves a big challenge: to fit as many 2-player games as possible in 1kb. Who knows, we could maybe fit three, four, five simple games in 1k...?

People have already made awesome 2-player games for JS1k (1, 2, 3, 4, 5, 6, 7, 8, 9), but we've never seen game compilations, and that's why I chose to try that. I chose five board games: Tic-Tac-Toe, Tic-Tac-Toe 3D, Four in a row, Reversi and Checkers.

Then, we tried to implement those games separately, in good old HTML and JS (the boards are made with tables, not drawn on the canvas):
We quickly (and sadly) abandoned the last two games (reversi and checkers) because the first three already needed more than 2kb.
We gathered those 3 games into one, and golfed them as much as possible, until the whole demo could fit in 1kb. This process included reducing the graphics to the strict minimum (just tables, X and O's). It actually went under 750b, and from that point, we had to decide whether to enhance the graphics or add a fourth game. We decided to make 4 games in one (because that would be impressive), and make a 2k version with great graphics.
Besides the graphics, almost all our efforts have been made on the 1k version and its 4 games. Actually, we golfed that demo on three levels!

1st level of golf: JS optimisation

The first goal is indeed to have the shortest JS code possible.
A lot of effort has been made to fit the menu + the four playable games in 1613b.

The games needed a lot of variables, and we had to introduce 23 global vars. Almost all the alphabet, and most of them are heavily reused.

To save bytes and avoid any kind of DOM manipulation, all the elements on the screen are redrawn after each player's move.

Here's the program's structure:

// Draw function
  // Init HTML (menu)
  // When a button of the menu is clicked, the game state is reset,
  // and the model m (an array representing the board) is initialized.
  h="<center><font face=arial><p><button onclick=m=[];f=2;a(p=g=s=1)>XnO<button onclick=m=[];f=3;a(p=g=s=1)>XnO3D<button onclick=m=[];f=1;a(p=g=s=1)>Find4<button onclick=m=[];f=w=0;m[27]=m[36]=-1;m[28]=m[35]=1;a(p=g=s=1)>Flip</button><p>";
  // If a game has been started

    // write table HTML (the board) according to the model
    // ... snip ...
    // Write informations under the board (current player, winner, ...)
    // ... snip ...

  // Update page's <body>

// Show the menu on load

// Onclick (when a player clicks on the board to play)
  // If the game is not over and the cell is empty
    // Current player plays current game and updates the model
    // ... snip ...

    // Change player
    // Redraw everything

The board drawing snippet is very simple, and made of three loops. One for the boards, another one for the lines, and a last one for the cells of each line, depending on the model. "f" represents the current game (0: reversi / 1: find 4 / 2: XnO / 3: XnO 3D).

<P>, <TR> and <TH> elements don't need to be closed.

<TH> elements have the advantage of being bold and centered, but their borders are hidden when they are empty. That's why we use the blank character \xa0 in the empty cells. (i.e. the 0's in the model.)

// Loop on tables, write table HTML

  h+="</table><p><table border>";

  // Loop on lines

    // Write line HTML, loop on cells
      // Write cell HTML
      h+="<th width=20 onclick=q("+[l,7-j,7-k]+") id=t"+l+">"+"X\xa0O"[1+(m[l]=m[l++]||0)];

The rules of each game were reduced to their minimum after a lot of iterations. After each click, the game updates the model and checks for a victory:

Tic-Tac-Toe, Tic-Tac-Toe 3D:


      // In the model, the players marks are represented as 1 and -1.
      // If 3 or -3 is found in this array representing all the possible alignments,
      // it means that a player has won.
        m[k+j]+m[k+j+3]+m[k+j+6], // Columns 2D
        m[k+l]+m[k+l+1]+m[k+l+2], // Lines 2D
        m[k+4]+m[k+0]+m[k+8], // Diagonals 2D
        m[k+4]+m[k+2]+m[k+6], // Diagonals 2D
        m[l+10]+m[l]+m[l+20], // Lines 3D
        m[l+10]+m[l+2]+m[l+18], // Lines 3D
        m[i+12]+m[i]+m[i+24], // Columns 3D
        m[i+12]+m[i+6]+m[i+18], // Columns 3D
        m[13]+m[0]+m[26], // Diagonals 3D
        m[13]+m[2]+m[24], // Diagonals 3D
        m[13]+m[6]+m[20], // Diagonals 3D
        m[13]+m[8]+m[18], // Diagonals 3D
        m[l+i]+m[l+i+9]+m[l+i+18] // Same cell in all tables
      ].indexOf((m[c]=p)*3) // here, we update the model and test the alignments
    g=0; // game over

Find four:

// If a wrong cell is clicked, apply gravity

// Test if 4 marks are aligned
        j<4&&m[k]+m[k+1]+m[k+2]+m[k+3], // Horizontally
        i<3&&m[k]+m[k+7]+m[k+14]+m[k+21], // Vertically
        i<3&&j<4&&m[k]+m[k+8]+m[k+16]+m[k+24], // Diagonally 1
        i<3&&j>2&&m[k]+m[k+6]+m[k+12]+m[k+18] // Diagonally 2


// Reset cell's playability

// For each direction

    // Reset that direction's playability
    // If the neighbour is the opponent

      // Loop on the next neighbours in that direction
      // If current color is found, stop, good direction
      // If an empty cell is found, stop, bad direction

      // If this direction is playable

        // Loop on the opposite neighbours

          // Toggle them

// Compute score

After each move, all the screen is updated, including the board and the game status (leader, winner, draw, pass, etc...)

// Board not full

    // Game not over: show next
    "XnO"[p+1]+" next<p>"
    // Game over (not reversi): show winner
    "XnO"[-p+1]+" won"
  // Reversi
    // Leader, pass button
    h+=(w?w>0?"O>X":"X>O":"O=X")+"<p><button onclick=a(p=-p)>pass";

// Board full

  // Not reversi: show winner or draw
  (g?"draw":"XnO"[-p+1]+" won")
  // Reversi: show winner or draw
  (w?w>0?"O won":"X won":"draw")

All this code was minified with Google Closure Compiler and optimized manually.

2nd level of golf: RegPack optimization

The JS source could be much smaller than that, but when you deal with JS packers, the main rule is not to be short, it is to repeat yourself as much as possible. That's why many snippets of the code are arranged (and sometimes "unoptimized" to have a better result in a JS packer.

3rd level of golf: make the packed code smaller

During the development of this game, RegPack v3 was being developed by Siorki. Subzey and I used this tool up to its limits and helped the author improving it via Github. We also tweaked the regexp in the final result to lose a few extra bytes and fit the whole thing in 1k!

for(_='/** snip **/';g=/[^ -?DHLMOTX[-~]/.exec(_);)with(_.split(g))_=join(shift());eval(_)

The 2K version

The only change in the "++" version is high-quality graphics. Here, the boards and the players marks can get the right shape, colors, borders, texts, which enhances the gaming experience a lot. Here's the code that changed in the "redraw" function:

// Reversi

  h+="<table border>";
      h+="<th width=20 onclick=q("+[l,7-j,7-k]+") id=t"+l+" bgcolor=#5b5>"+["<table bgcolor=0 width=20 height=20 class=i></table>","\xa0","<table bgcolor=#fff width=20 height=20 class=i></table>"][1+(m[l]=m[l++]||0)];


// 4 in a row
  h+="<table cellspacing=5 bgcolor=#aad>";
      h+="<th width=20 onclick=onclick=q("+[l,7-j,7-k]+") bgcolor=#fff style='border-radius:50%;overflow:hidden'>"+["<table bgcolor=red width=20 height=20 class=i></table>","\xa0","<table bgcolor=yellow width=20 height=20 class=i></table>"][1+(m[l]=m[l++]||0)];

// Tic Tac Toe
  h+="<table style=border-collapse:collapse>";
      h+="<th width=20 onclick=q("+[l,7-j,7-k]+") style='"+(j?"border-bottom:2px solid;":"")+(k?"border-right:2px solid;":"")+"'>"+"X\xa0O"[1+(m[l]=m[l++]||0)];

// Tic Tac Toe 3D
    h+="<table border style=margin-left:"+(4*(1-i))+"em>";
        h+="<th width=20 onclick=q("+[l,7-j,7-k]+")>"+"X\xa0O"[1+(m[l]=m[l++]||0)];

// Board not full

    // Game not over: show next
    "Next player: <b>"+
      ["<table bgcolor=0 width=20 height=20 class=i></table>","\xa0","<table bgcolor=#fff width=20 height=20 class=i></table>"],
      ["<table bgcolor=red width=20 height=20 class=i></table>","\xa0","<table bgcolor=yellow width=20 height=20 class=i></table>"],
    // Game over (not reversi): show winner
      ["<table bgcolor=0 width=20 height=20 class=i></table>","\xa0","<table bgcolor=#fff width=20 height=20 class=i></table>"],
      ["<table bgcolor=red width=20 height=20 class=i></table>","\xa0","<table bgcolor=yellow width=20 height=20 class=i></table>"],
    ][f][-p+1]+"</b> won!"
  // Reversi
    // Leader, pass button
    h+="<p><button onclick=a(p=-p)>Pass";

// Board full

  // Not reversi: show winner or draw
    ["<table bgcolor=0 width=20 height=20 class=i></table>","\xa0","<table bgcolor=#fff width=20 height=20 class=i></table>"],
    ["<table bgcolor=red width=20 height=20 class=i></table>","\xa0","<table bgcolor=yellow width=20 height=20 class=i></table>"],
  +" won!")
  // Reversi: show winner or draw
  (w?w>0?"<table bgcolor=#fff width=20 height=20 class=i></table> won!":"<table bgcolor=0 width=20 height=20 class=i></table> won!":"Draw!")

That's all folks! See ya!

Maxime and Anton

JS1k 2014 post-mortem: Floppy Dragon

febuary 2014

Welcome to the making-of of Floppy Dragon, my individual entry for JS1k 2014.

In febuary 2014, all the Internet was talking about the game Flappy Bird, so I decided (for fun) to make a mini-game inspired by that, but featuring a dragon instead of a bird, and stalactites in a cave instead of pipes. I also wanted to introduce many cool details such as responsive display, nice animated graphics, random maps, increasing speed and difficulty, textual interface, etc. And, of course, it had to fit in 1k of JavaScript.

Source code

Commented (2876b)

Minified (1507b)

Packed (1024b)

Responsive display

In JS1k entries, a represents a fullscreen canvas, and c represents the 2D context of this canvas. On load, the canvas' width and height are automatically set to the innerWidth and innerHeight of the window. There are many ways to draw into this canvas, but I chose the following approach:
First, I scale the canvas' context with this ratio:

Then, the game just has to draw on the canvas' context as if it was 1000px high and infinitely long, and the drawing will spread on all the screen.

For the orange background, I simply draw a huge rectangle on the canvas when I start rendering each frame:

// Background

Random map

In the source code of Floppy Dragon, there's a little piece of code that initializes the game. This code is executed on load, but also when the player loses and decides to try again. In this snippet, I generate an array g with 1000 random integers between 0 and 7, stored from index 0 and index 1000:


In the game loop, this array is used as a heightmap to draw the upper and lower walls of the cave, and the obstacles:
A new "step" is drawn every 40px to make the walls look less flat, and an obstacle is drawn every 20 steps. All these points depend on the heightmap values. The obstacles use these values x 100.

To sum up, the walls are just two big polygons that are drawn with a very similar code, the only difference is that they are mirrored and show the obstacles in a complementary way (a 300px gap is kept between each pair of stalactites) In the following code, k represents the horizontal offset of the scene. This rendering is performed at each frame.

// Draw the walls

// Roof
  // Wall
  // Obstacle

// Ceiling
  // Wall 
  // Obstacle

The maps repeats itself every 1000 steps, thanks to this code that goes 4000px back as soon as the game scrolls more than 4000px:

// Loop on the map if we are far enough

User interface

The user interface is made of different texts showing the game name, the score, and a start / restart message. These texts are centered and scaled. In the following code, d is set if the game is started, f is the score, and e is set if the game is over.

// Text
  c.font="6em Arial";

User interface also means controls, and the controls are simple: press any key or click or tap to start, flap, or restart. When those events are fired, the "reset sequence" is executed (if the game is over), or the dragon flaps (if the game is running, or starting). h represents the vertical speed of the dragon, i and j represent its coordinates, and l is the game's speed.

// Controls
  // If game is over, reset
  // If game is not over, start the game if it's not started, and jump

Talking about framerate, as I explained earlier, the game's speed is increasing. A variable (l) holds the number of milliseconds between each frame. It starts at 50 and is decremented after each obstacle passed, with a lower limit of 20. The rendering function is called recursively with the corresponding timeout:

// Game loop

  // ... snip ...
  // Increase score and game speed after each obstacle
  // ... snip ...
  // Loop

At each frame, we also apply gravity (i.e. increment the vertical coordinate of the dragon)

// Apply gravity if the game has started

Finally, we have to detect when the game is lost (when the dragon's head hits the floor or the ceiling or an obstacle. Here's how it is done:

// Game over

Draw the dragon

Last but not least, when all the game mechanics are ready, and the code is minified and packed, I have only 360 bytes left to draw the hero of the game: the dragon. I thought I could simply use a unicode character such as U+1F409 or U+1F432. But these models are not very good-looking, and they aren't displayed correctly on all browsers.

I could also use pixel art (like this) or bezier curves (like that) but this would have used too many characters to draw what I had in mind.

So I decided to draw a shape made of tiny lines, and compress it with a home-made technique:

The shape starts at the neck of the dragon. Each point of the shape has coordinates very close to the previous point. My goal was to store the X and Y coordinates of each point in an unique ASCII character. So I stored the X offset in the first 3 bits of each character (range: -4 to +4), and the Y offset in the last 4 bits (range: -8 to +8).

As a result, I was able to represent the dragon's body plus the two different wings shapes in just 196 characters (most of these are invisible).


And here is the code that draws the full shape at the good coordinates. Note that the coordinates are inverted when the dragon dies, in order to draw the falling animation.

  for(o in p){

To finish, a few final optimizations and JScrush helped me fit this game in 1024 bytes, and I submitted it two weeks after the beginning of the contest. It had the honor to be mentioned here and here by Peter van der Zee, who organizes JS1k every year.


Implicit getElementById's

october 2013, october 2016

What is the most useful JavaScript function, but also the most annoying to write? document.getElementById(), of course! It is so useful, and so long, that every decent JS library contains an alias to call it easily (generally $()).

But did you know that we often don't even need to call it? Indeed, all the modern browsers (including IE6+) allow to access any element by calling directly its id as a global var! Surprisingly, it's even in the specs!


<div id="myId"></div>
  alert(myId);                            // --> [object HTMLDivElement]
  alert(;                         // --> "myId"
  alert(;                  // --> "myId"
  alert(;                    // --> "myId"
  alert(;                    // --> "myId"
  alert(;                     // --> "myId"
  alert('myId' in window);                // --> true
  alert(window.hasOwnProperty("myId"));   // --> true

So "myId" is clearly a property of window! However, it's not "enumerable", so we can't see it if we loop or make a console.dir() on the window object:

// Display the content of window:
console.dir(window);                      // myId is not présent.

// Loop through the properties of window:
var present = false;
for(var i in window){
  if(i == 'myId'){
    present = true;
alert(present);                           // --> false

It's also important to note that if an id is not a valid JS identifier (for example if it contains a "-" or starts with a number), you can't access it directly, but you can still access it with window["invalid-identifier"].

For the record, Mathias Bynens wrote a very interesting article about ids and Unicode. Crazy!

On my side, I experimented with this "feature" and found some funny things:

An implicit id can't have the same name as a protected JS keyword:

<div id=function></div>
<script>alert(function) // SyntaxError: Unexpected token )</script>

An implicit getElementById can't overload a native global var, such as window.screen:

<div id=screen></div>

  screen.innerHTML = "screen";          // --> nothing happens
  alert(screen);                        // --> [object Screen]

Also, it can't redefine a global var defined earlier

  global0 = 1;

<div id=global0><</div>

  global0.innerHTML = "global0"; // --> nothing happens
  alert(global0);                // --> 1

A global var defined with the "window." prefix can overload an implicit getElementById

<div id=global1></div>

  global1.innerHTML = "global1"; // --> the div contains the text "global1" 
  console.log(global1);          // --> [object HTMLDivElement]
  window.global1 = 1;            // overload global1
  console.log(global1);          // --> 1

If we try to overload an implicit getElementById with a global var defined without any prefix, this is what happens (the results differ according to the browser used):

<div id=global2></div>

  global2.innerHTML = "global2"; // --> the div contains the text "global2" 
  try{global2 = 1}               // Try to overload global2
  catch(e){alert(e)}             // --> TypeError on IE < 9 / no error on other browsers
  console.log(global2);          // --> [object HTMLDivElement] on IE < 9 / 1 on other browsers

Finally, if we try to overload an implicit getElementById with a global var defined with a "var", this is what happens:

<div id=global3></div>

  global3.innerHTML = "global3"; // --> TypeError: Can't set property 'innerHTML' of undefined
  console.log(global3);          // --> undefined
  var global3 = 1;               // --> Try to overload global3
  console.log(global3);          // --> 1

As you can see, even if the var global3 is defined later in the code, browsers hoist variable definitions at the beginning of their scopes, and that's why it still breaks the lines 1 and 2.

To conclude, this technique is useful in some particular cases where we don't want to use a heavy library nor call the long function document.getElementById(), especially in code golfing contests. But in real life, it's safer to use the good old native methods because the implicit document.getElementById comes with several traps, and can lead to hours of debugging...


The joys of text-shadow... on IE.

septembre 2013

We don't present text-shadow anymore, this CSS property allowing to set a shadow to a text.
(see: the specs, the MDN page)

Many browsers have some small implementation problems, but the worst are, of course, the old IE. More precisely, IE < 10.

However, those browsers have access to proprietary "filters", created by Microsoft, and allowing to achieve a similar effect.
Sometimes, we have no other choice than using them. This article explains how.

Two filters allow to simulate shadows: Shadow and Glow.

Here is the "Shadow" version:


h1 {

  text-shadow: 2px 2px 3px #ff0000; /* IE10+, Firefox 3.5+, Opera 9.5+, Chrome, Safari, and mobile browsers */
  filter:progid:DXImageTransform.Microsoft.Shadow(color='#ff0000', Direction=135, Strength=3); zoom: 1; /* IE 6-9 */


<h1>Shadowed title</h1>


Shadowed title

If you look at this wonderful demo on an old IE (or on IE10 in legacy mode) you will see a shadow. But you will also notice that the text became all pixellized, and unfortunately, there is no solution for that.

Please note that "Direction" is set in degrees (for example, 0 = up, 90 = right, 180 = bottom, 270 = left) and "Strength" vaguely corresponds to the shadow's spread. (in reality, it looks more like a perspective effect...)

And here is the "Glow" version


h1 {

  text-shadow: 0px 0px 1px #ff0000; /* Good browsers */
  filter: glow(color=#ff0000,strength=3); zoom: 1; /* IE 6-9 */


<h1>Glowing title</h1>


Glowing title

The Glow filter mainly creates a halo around the text. But it's still quite ugly on IE. (you can have something less ugly if you don't exaggerate this much on the colours)

Note that the filters can be combined, like the CSS3 text-shadows. It's useful, especially when we want a "text-stroke" effect.

h1 {

  text-shadow: 1px 1px 1px #ff0000, 1px -1px 1px #00ff00, -1px -1px 1px #0000ff, 1px 1px 1px #f0000f; /* Good browsers */
  filter: progid:DXImageTransform.Microsoft.Shadow(color='#ff0000', Direction=0, Strength=1), progid:DXImageTransform.Microsoft.Shadow(color='#00ff00', Direction=90, Strength=1), progid:DXImageTransform.Microsoft.Shadow(color='#0000ff', Direction=180, Strength=1), progid:DXImageTransform.Microsoft.Shadow(color='#f0000f', Direction=270, Strength=1); zoom: 1; /* IE 6-9 */


<h1>Titre with many shadows</h1>


Titre with many shadows

Keep in mind that it's very verbose, and very ugly on IE, when it's not used correctly.

Besides, I have an exclusive bug to show you:

These browsers only work on block elements, not on inline elements.

So if you need shadows on a <span>, you'll have to write some nonsense like... span { display: block }.


Ordered and unordered lists

august 2013

HTML lists... we all know them by heart: they exist since the first versions of HTML and XHTML, and they almost didn't change since then. They also work on all browsers.

Let's study their HTML.
<ul> means "unordered list", <ol> means "ordered list", et <li> means "list item".
Here is their simplest form (note that the </li> are optionals)


  <li>Element 1
  <li>Element 2
  <li>Element 3

  <li>Element 1
  <li>Element 2
  <li>Element 3
  • Element 1
  • Element 2
  • Element 3

  1. Element 1
  2. Element 2
  3. Element 3

The li can also contain ul or ol sub-lists. (but we'll talk about that later)

The lists also have specific attributes:

  • <ul> (and its <li>s) can have a "type" attribute, with the value "circle", "square" or "disc". This allows to change their ticks ("circle" by default).
  • <ol> (and its <li>s) can also have a "type" attribute, with values like "A", "a", "I", "i", "1". This allows to set their number notation ("1" by default).
  • You should avoid the type attribute, and use CSS instead (we'll come to this later)
  • <ol> can have a "start" attribute telling where the numbering should begin, and its <li>s can have a "value" attribute to change their default number, and the following ones. (This works on IE6+)


<ol start="10">
  <li>Element 10
  <li>Element 11
  <li>Element 12
  <li value="20">Element 20
  <li>Element 21
  <li>Element 22
  <li value="5">Element 5
  <li>Element 6
  <li>Element 7
Ce qui donne:
  1. Element 10
  2. Element 11
  3. Element 12
  4. Element 20
  5. Element 21
  6. Element 22
  7. Element 5
  8. Element 6
  9. Element 7
  • And finally, <ol> can have a "reversed" attribute (appeared with HTML5) allowing to count the elements from bottom up. But since it doesn't work on IE < 11, you should rather set a decreasing "value" to each <li> to get the same effect.

Now let's see their default CSS code, and their dedicated CSS properties:

<ul> and <ol> are normal blocks (display: block). They have a default padding-left that depends on the browser (around 40px). The ticks are included in this padding and they can't be selected as normal text with the mouse.

<ul> has disc-shaped bullets (list-style: disc outside none).

<ol> has numbers in front of each element (list-style: decimal outside none).

<li> have a special display (display: list-item), almost identical to display: block. They also have a list-style (inherited), but they can overload it individually.

The list-style property is a shorthand notation allowing to set three parameters:

  • list-style-type (that can have many many values),
  • list-style-position (that can have the value "inside", "outside" or "inherit". Inside allows them to contain their own ticks, instead of letting them stay in their parent's padding.)
  • list-style-image, which is "none" by default, but it can also be the url of an image. If the URL is invalid (or if the image isn't fount), there is a fallback on the default list-style-type (disc, decimal, ...).

Now let's see the different problems that lists can cause us:

 • If you apply a CSS reset to your page, it will delete the paddings on ul and ol elements, and their ticks will be hidden.
Solution 1: put the ticks inside the li with this rule:

ul, ol { list-style-position: inside }
The ticks reappear, but we still lose the indentation of nested lists.
  • list
  • list
    1. sub-list
    2. sub-list
Solution 2 (the good one): don't change the ticks position, but re-set a padding-left to the lists, like this:
ul, ol { padding-left: 2em }
  • list
  • list
    1. sub-list
    2. sub-list
Indeed, the padding-left are cumulative, and so the nested lists are correctly indented.

 • If we nest ordered lists, their numbers will stay the same (decimal notation). A good typographic practice is to do this:

ol { list-style-type: decimal }
ol ol { list-style: upper-alpha }
ol ol ol { list-style:  lower-roman }
ol ol ol ol { list-style: lower-alpha }

Which allows nested lists as pretty as this, with up to four levels:
  1. level 1
  2. item
  3. item
    1. level 2
    2. item
    3. item
      1. level 3
      2. item
      3. item
        1. level 4
        2. item
        3. item

 • The CSS resets generally set a vertical-align: baseline and a line-height: 1.5em to all the elements of the page, and this causes a problem on IE < 8 when we have nested lists.
Indeed, if you watch the following list on IE < 8, you will see the numbers "fall" at the bottom of the sub-lists, and the ticks will be misaligned with the text ahead.

  1. level 1
  2. item
  3. item
    1. level 2
    2. item
    3. item
      1. level 3
      2. item
      3. item
        1. level 4
        2. item
        3. item

To fix the "falling" ticks, use this CSS dode:

ol, ul, li { vertical-align: top }

To fix the alignment between the ticks and the text, there is no miracle solution, but five possible approaches:

  • Consider that an offset of a few pixels is not a big deal on IE < 8, and do nothing.
  • Put a "1em" line-height on all the page.
  • Let the default line-height to "1.5em", but set the <li>'s line-height to "1em" and their padding to "0.25em 0", which will do the job, unless the text of the <,li> spreads on two lines or more, because these lines would be very close.
  • Replace the default tick with an image that looks like a tick, but with a few pixels of vertical offset.
  • Or... (and this is the solution I recommend) put an element (like a <span>) in each <li>, and apply this style:
    li span { *position: relative; *top: -0.5em }
    • Item 1
    • Item 2
    • Item 3

However, there is a bug on all the NEWER versions of IE (except IE6 and 7!), which you can see here:

  1. level 1
  2. item
  3. item
    1. level 2
    2. item
    3. item
      1. level 3
      2. item
      3. item
        1. level 4
        2. item
        3. item

Each sub-list has a huge gap on top. It's not a margin or a padding, it's an IE bug. To get rid of it, you can set float: left to the lists (but it could lead to flow issues), or put them in display: inline-block (that's what I recommend):

li ul, li ol { display: inline-block }

So, everything is correct now.

Other list-related bugs and their solutions are described here, but I won't talk about them because they are too much specific.

Instead, let's talk about the original things that we can do with HTML lists:

  • A <li> can live without a <ol> or a <ul> around it. In this case, it will have a circle-shaped tick (on all the browsers).
  • A <ul> / <ol> can contain elements that are not <li>. (or even some text). These elements won't have a tick or a number.
  • There are several easy ways to display a list on many columns.
    This article is exhaustive on this subject.
  • A list can look like a file tree with a simple 1px image and some ingenious CSS code: take a look at this.
  • The <li> elements can use one (or many) unicode character(s) as their ticks, thanks to CSS3 (and it works on IE8+):
    li { list-style: none }
    li:before { content: ">>"; margin:0 1em 0 -3em;  }
    • Item 1
    • Item 2
    • Item 3

I'll stop here, and I invite you to look at my last project, Basis: a basic "boilerplate" for HTML/CSS/JS pages, with a custom CSS reset that contains all these best practices, and many others.


The HTML5 contenteditable attribute

july 2013

The contenteditable attribute makes any HTML element editable with the mouse and/or the keyboard.

This attribute is one of the HTML5 global attributes, so it can be applied to all HTML5 elements.
It's also one of those features that existed in browsers for a long time before W3C standardization.
Indeed, we can use this attribute on browser as old as IE 5 and Firefox 3! Microsoft created it, by the way.

It's easy to use: like id or class, you can use contenteditable="true" on any element, or simply contenteditable.
Note that this attribute is inherited by default. So the value contenteditable="inherit" is completely useless (but it exists).
However, if you want to make an element editable, but not its children, these children must have contenteditable="false".

Example: click on this paragraph to edit the text inside.

Here's the list of crazy facts about contenteditable:

  • Like we saw earlier, we can make an element non-editable if its parent is editable. The non-editable element can still be selected, moved and deleted.

    Example: In this editable paragraph, this text in bold can't be edited, but you can move and delete it with the mouse.

    On Firefox, we can't move text from an editable container to another editable container... that's a bug.
  • Firefox allows to resize images that have or inherit the contenteditable behavior.


    Click on this image to resize it.

  • Any HTML code can be inserted in an editable element
    Example: You can copy-paste here

    - The content of another Webpage:

    - The content of a Word / Excel document:

  • Thanks to this attribute, we can make complete WYSIWYG editors, like CKEditor.
  • You can also access to a quick notepad by typing data:text/html, <html contenteditable> in the address bar of your browser (or by clicking here). This trick comes from Jose Jesus Perez Aguinaga. You can find many variations of this notepad here.
  • If you make a style element visible and editable, this can make a crazy auto-recursive editor, in which you can edit in real time the style of the block you're writing into.
    Try it:
  • script elements can also be visible and editable (here's an exemple). However, the JavaScript code isn't executed when you edit it. It's a simple text block.

    (open your F12 console to see this message)
    But JavaScript itself allows to detect changes happening in a script element and execute its content.
    Warning: don't do this at home (XSS attacks can occur). This example is only a proof of concept.

    This works fine on Chrome and IE 10. Sadly, Firefox and IE9 can't listen to keystrokes events on script elements... But you can replace them with textareas, which are natively editable.


Le doctype HTML5

juin 2013

Le doctype n'est pas un élément HTML comme les autres, c'est un préambule qui sert à dire au navigateur comment il doit afficher le reste de la page.

Ce préambule existe depuis HTML2 et XHTML1. Toutefois, jusqu'à l'apparition d'HTML5, les doctypes étaient de très longues chaînes impossibles à mémoriser par coeur, et pour peu qu'on fasse une faute de frappe, les navigateurs pouvaient mal le prendre, en particulier IE qui applique des règles d'affichage ancestrales (qu'on appelle aussi "mode quirks") en cas de doctype absent ou invalide.

Bref: ce qu'il faut retenir, c'est que maintenant, le doctype s'écrit comme ceci: <!doctype html>, et qu'il doit être placé tout au début du document HTML. Il est suivi de l'élément racine, à savoir <html>.

Oui mais comment on fait du XHTML5 alors? La réponse est simple: ça n'existe pas. Il n'y a qu'un HTML5, et il n'est ni "X" ni "strict" ni "transitional". et ça ne l'empêche pas de bien marcher, même sur les IE préhistoriques.

Voici maintenant la liste tant attendue des trucs de ouf concernant le doctype HTML5:

  • Le doctype est insensible à la casse et peut contenir plusieurs espaces avant et après le mot "html". Vous pouvez donc l'écrire <!DoCtYpE     HtMl   > si ça vous chante.
  • Le doctype HTML5 peut contenir d'autres informations, mais aucune n'est utile quand on écrit son HTML à la main ou avec un IDE moderne.
  • Le doctype et l'élément title sont les deux seuls éléments obligatoires dans un document HTML5 valide.
    Voici le plus petit valide document HTML5:
    <!doctype html>
  • Le doctype ne peut pas être lu, ni modifié en JavaScript, mais on peut tout de même savoir si le navigateur est en mode quirks ou en mode compatibilité comme ceci:
    if(document.compatMode === 'CSS1Compat'){
    	// On est pas en mode quirks!
    if(document.compatMode === 'BackCompat'){
    	// On est en mode compatibilité!
    (si les deux sont "false", tout va bien)
  • A propos du "mode compatibilité", qui pousse IE7, IE8, IE9 et IE10 à afficher les pages Web avec le moteur de rendu de leur prédécésseur (E6, IE7, IE8 et IE9, respectivement), de nombreuses raisons font que ce mode s'active sur IE, mais le plus souvent, c'est dû au fait qu'on visionne une page HTML qui se trouve sur son PC, ou sur un réseau local. Pour éviter ce désagrément, ouvrez IE, faites "alt" pour que le menu du haut apparaisse, allez sur "outils > paramètres d'affichage de compatibilité" et décochez "Afficher les sites intranet dans Affichage de compatibilité". Faites "OK" et redémarrez IE. C'est réglé!
  • Sur IE < 9, le doctype, du fait qu'il commence par un !, est considéré comme un commentaire HTML. En voici la preuve:
    // Récupérons le nom du premier élément HTML de cette page dans le DOM
    var premier_element = document.getElementsByTagName("*")[0].tagName;
    ("!" sur IE < 9, "HTML" ailleurs)

    (Les IE < 9 sont d'ailleurs les seuls navigateurs à inclure les commentaires (sauf les commentaires conditionnels) dans document.getElementsByTagName("*"), comme décrit ici)
  • Le doctype HTML5 fait apparaitre de vilaines scrollbars autour des iframes sur IE6, pour d'obscures raisons, alors que les doctypes d'avant HTML4 ne le faisaient pas. Le seul moyen de les faire disparaître est d'utiliser l'attribut (hélas non standard) scrolling=no sur vos iframes... si vous voulez supporter IE6, bien sûr, ce que je ne vous conseille pas de faire.
  • Démo: iframe normale

    iframe sans scrollbars sur IE6
  • Il y a un autre bug sur IE, y compris IE10 (et peut-être IE11, à confirmer): si votre page est en mode quirks, et qu'elle contient des iframe qui ont un doctype valide, ces iframe s'afficheront tout de même en mode quirks!

A bientôt!