I wrote another 1K JavaScript demo: a 1k version of poptart cat. See this site for more information about this meme.
Minimization tricks include:
- storing all sprites in a string by encoding each pixel in a sprite in 2 bits and storing 6 bits in each character. Each 2 bit chunk can have one of these values: 0=CR/lF (end of line/start of next line in sprite), 1=transparent (do not draw but move to next pixel), 2 = color 1, 3 = color 2. The colors are chosen from a palette, which is a string containing a number of RGB colors encoded in 3 hex chars (‘fff’=white, ’000′=black, ‘f00′=red, etc…). Each sprite has an associated palette based on the number of the sprite. Most sprites use one of two palettes: a one color palette (‘fff’) for the background stars) or a two color palette (’999000′) for the cat’s face, paws and tail. A few other palettes are used for the poptart.
- storing all sprite maps (meaning which sprites to draw where for each frame) in an array of 6 strings, one for each frame, by encoding each position and sprite number in 3 bits. The background stars are not included; their vertical position is based on their number, by multiplying the number of each star by a certain value. Their horizontal position is based on time, by multiplying the time by a certain value modulus the width of the image and adding a “random” value using the value of a different character from the sprites string for each star.
- drawing squares instead of sprites to fill larger regions, as sprites need a lot more bytes to store when they get bigger then code to draw a single square does.
- using one function for two purposes: to draw a specific sprite at a specific location using a specific palette on the canvas, or to draw a square at a specific location, with specific dimensions and a specific color on the canvas. The function will draw a sprite if there are only 4 arguments provided and a square if there are 5.
- using a bit of math to offset multiple sprites based on which frame is being drawn and adding sprites to the sprite. This is done to have the individual sprite maps for each frame contain the same information as much as possible, which makes them easier to compress.
I assumed it would be easy to code this one in 1K, but it turned out that it was pretty hard to minimize my code.
- I started with a large file that draw only the (animated) cat. This wasn’t optimized in any way: it used one big sprite for each frame, with each pixel being stored as byte containing a value 0-8, representing an index into a palette.
- I then cut the cat into smaller individual sprites that I could reuse between frames (eg. the face is always the same, just at a different location). I stored a sprite mapping, meaning which sprite to draw where for each frame. I stored all this information in a string using values between 0-36 encoded using ‘[char] = [value].toString(36)” and read using “[value] = parseInt([char], 36)”.
- I chose to give each sprite only two colors + transparent (3 values), so I can store each pixel in 2 bits (4 values) and use the unused value to signal ‘end of line’ and using ‘end of line’ twice for ‘end of sprite’. This meant I could store the entire sprite as 2 bit values. I encoded 3 of these 2 bit values per byte using “[char] = String.fromCharCode([values]+offset)” and red them using “[values] = [char].charCodeAt(0)-offset”.
- I stored the values in the sprite maps as 3 bit values and encoded 2 of these 3 bit values per byte as well.
- I wrote code that choses an ‘offset’ to use in encoding sprites/sprite maps that caused the resulting string to contain as many of the same bytes as the code, which allows better compression.
- At this point I was down to ~1.2K compressed. By reusing code for two purposes (eg. the ‘f’ function) and rearranging/rewriting code to have it contain as many repeated strings as possible to allow better compression, I as able to get it down to ~1.01K.
- Finally, I optimized my png compressor code to work together with the poptart cat code; eg. do not use ‘eval’ to run the decompressed code, which then uses setTimeout to start the animation, but use ‘setTimeout’ directly. This allowed me to squeeze everything into 1K.
Because manually editing 2/3 bit values stored in 8 bit characters + offset is impractical, and because some of the values where calculated rather than hand-picked. I wrote my development code to generate the final code and data automatically. This means I have a somewhat documented, readable version of the test container HTML and the actual JavaScript code. The test container will load the code, which will show the animation slowed down to 1 frame per second and output the final code below the animation. The final code is what I pumped through my to-be-released JsSfx png compressor.
Afterwards, I realized that there are a few ways in which I could have improved on the code by doing a few things differently:
- Save data as unicode strings; this allows storing not 6 bits per byte, but (effectively) 16 per 2 bytes. This could reduce the size of the data by about 128 bytes, but would use more bytes to store the non-unicode characters outside of the strings.
- Save each entry for each sprite map in a separate string, meaning store the x coordinates in one string, the y coordinates in another and the sprite number in a third. This should result in data that can be compressed better.
- Use scaling for sprites to be able to draw the background, rainbow and body of poptart cat using sprites as well instead of separate code to draw squares. This would use less bytes than having code to draw the cat using both sprites and squares.


Leave a Comment