Sprite Sheet Gifs



My current workplace makes heavy use of emojis in our Slack, and as I was thinking about adding more, I thought that old Nintendo pixel art would be a really good fit. Pixel art is already optimized for small display areas, and reads cleanly in those small reaction areas. Nintendo is also easily recognized and has a huge collection of great characters.


I found a website that has ripped or recreated a bunch of sprites from those old SNES games. However, saving each image one at a time was a pain, so I updated my site crawler to download all the sprites on a page.


Once I had a spritesheet, the next step is to combine the desired frames into a gif. Ideally I'd use the wonderful pixel art program Aseprite to read in the tile sheet as a grid and then export the gif. However, I soon realized that none of the tilesheets I had downloaded were in uniform grids. (It makes sense that extracting/recreating assets would not care about making the grid uniform since the creators aren't thinking about reading these sheets programmatically).


I tried using the Gimp to make the gifs by hand, and while that worked, it was laborious and meant I had to line up every frame by hand. This got me thinkging about how 'easy' it would be to create an app that takes a tilesheet, determines a grid, and then reprints the sprites on that grid. I thought about it for the rest of that week and then spent the weekend building AutoSprite


Autosprite reads in an image and finds the background color by finding the color with the most pixels. It then finds all non-background pixels (foreground pixels or sprite pixels) and adds them to a list of 'seeds'. For each seed pixel, we walk all neighbor pixels and add them to the 'sprite' if they're also foreground pixels. Once we have a complete sprite, we remove all of its pixels from our list of seeds. We repeat that until we have no more seeds, and instead have a list of sprites.


The app then calculates the bounding box of a sprite by grabbing the min and max x and y values for any of its pixels. Once we have all the bounding boxes, we can figure out the minimum grid size needed to accommodate them all uniformly. With grid information we can now write a new image where the sprites are all spaced uniformly based on the grid. I can then load these images into Aseprite and because the grid is uniform, all the frames line up, I can do any manual adjustments, and then export as a gif.


This method isn't perfect though. For one I lose some stray/orphan pixels (the app throws away sprites with less than 5 pixels etc). This is because I'm only crawling pixels in cardinal directions and I have no tolorance for background pixels. I also pick the largest grid for all sprites, so if a tilesheet has sprites of decently different sizes, the small sprites can inherit an overly large grid. I thought about inferring multiple grids, but it seems better to let the user do that by creating multiple input images. Finally, I may reorder sprites, which could mess up natural frame progressions in the tilesheet, but sadly I couldn't think of a way to understand and detect frame ordering.


After all that programming, I was too worn out to spend much time making gifs. Hopefully in the future I'll create a bunch more, like the ones in this post. Either way, it was a lot of fun to build these tools.