Building a sprite animator with URL-based sharing

16 October 2022

After building the boxworld.js example game I wanted to simplify the asset making process. After the usual scouring-the-web-for-the-perfect-tool and coming up empty-handed I decided to make it myself.

I wanted the following functionality:

  • 11x11 grid that can be painted
  • A timeline for animation
  • Sprite data stored in the URL

I like working within constraints, without them it’s easy to end up with an eternity project, which I have enough of already. I think 11x11 is enough pixels to draw something rough. I didn’t want this app to use a database, all data required to animate a sprite needs to be stored in the URL as a string.

The sprite

Gif showing sprite painting

A sprite is comprised of an 11x11 grid and is stored in the following format:

const sprite = {
  id: "abc123",
  name: "Hog on bicycle",
  palette: ["#000", "#ababab", "#c9c8c7", "..."],
  frames: ["aaaabbbaaaaaaaabbbaaaaaaaabbbaaaaaaaaabaaaaaabb...", "..."],

The frames refer to colors in the palette by charIndex, so a in this case refers to #000.


The most complex part turned out to be the sharing functionality. As usual, I found myself spending 90% of the time in a rabbit hole reading about the various ways of compressing a string.

I wanted to be able to share the animated sprites using the url, no matter how many colors or frames it had. The URL length has a limit, so I spent a lot of time obsessing over how to compress the length of the sprite data.

When a sprite is shared it’s passed through a function that does the following:

  • 1: Remove repetition

Replace repeated characters with a number, for example:

'aaaabbbaaaaaaaabbbaaaaaaaaccccccccc' becomes: a4b3a8b3a8c9

  • 2: Remove duplicate frames

If any frames match, replace the last occurance with 'x{index}' where index is the array position of the source frame.

  • 3: Look for repeated chunks (Abandoned)

This is the step I spent the most time on. I started out by writing a function that looks for repeated chunks between the frames, add these to a hash array and replace an occurrence with a ref/pointer.

I managed to save a little space with this method, but found that for some rare cases it actually increased the overall size.

In the end i abandoned this idea. The extra complexity introduced didn’t feel like it was worth it for the output.


The MVP is done, and it’s good enough for me to produce assets for future games. My plan now is to connect this project with the boxworld.js game engine and build a no-code UI for creating simple games, check back in 2049 for the results!

Illustration showing work in progress for no-code game editor