https://www.joshwcomeau.com/animation/sprites/ JoshWComeau * categories * courses * goodies * About Sprites on the Web Filed under Animation on in February 23rd, 2026. Feb 2026. Last updated on in February 24th, 2026. Feb 2026. Table of Contents IntroductionBasic implementationStep positionsUse cases Introduction In 2015, back when Twitter was still Twitter, their dev team had a problem. In those early days, tweets could be "favourited" by clicking a little "" icon. The product team wanted to migrate to "liking" tweets, Facebook-style, with a "[?]". As part of this update, their designers created this lovely animation: A heart goes from gray to red, swelling and spraying particles in all directions Pause This looks super nice, but there's kind of a lot going on in there; by my count, there are 16 separate elements all animating at the same time (14 particles, the popping circle, the heart). Twitter's web app needed to run on very low-end mobile devices, so it wasn't feasible to create this procedurally using DOM nodes. Instead, they decided to borrow a technique from video games: sprites. The basic idea with a sprite is that we create a single image that contains each individual frame of an animation in a long strip. Then, we display each frame for a fraction of a second, like a roll of film sliding through an oldschool film projector: Show each frame for:500ms [500 ] [twitter-li] Current frame of sprite animation In this blog post, I'll show you the best way I've found to work with sprites in CSS, and share some of the use cases I've discovered. We'll also talk about some of the trade-offs, to see when we shouldn't use sprites. Motion Warning Your operating system has the "Reduce motion" option enabled. I generally do my best to respect this preference on this blog, but for this post in particular, there are several motion-heavy animations that have not been disabled. These animations are necessary in order to understand the animation technique. If you have a motion sensitivity, please exercise caution with this blog post! Link to this headingBasic implementation First thing's first, we need an asset! Let's use a gold trophy sprite I created a few years ago: [gold-troph]A shiny gold trophy with a diamond and blue flames To produce the illusion that the fire is flickering, I drew five different versions of the blue flames. These frames are stacked side-by-side in a single image known as a "spritesheet": A hand-drawn gold trophy with blue flames. There are 5 copies side-by-side, with each trophy having a slightly different flame pattern Here's the fundamental strategy: we'll create an tag and calculate its size based on one of these frames. We can then use object-fit and object-position to control which part of the sprite is currently visible, flipping through each frame using a CSS keyframe animation. This full image has a native resolution of 2000px x 800px, and contains 5 frames. This means that each frame is 400px x 800px. In order for this image to look sharp on high-resolution displays, we'll want to cut this size in half, so our final image will be 200px x 400px. By default, tags will try to squeeze the entire image content into the DOM node's area, meaning we'll wind up seeing all 5 trophies, crammed together: Code Playground Format code using Prettier Reset Code HTMLCSS Focus the editor. This will trap focus until you press Escape.Code editor: A gold trophy with flickering flames [ A gold trophy with flickering flames [ A gold trophy with flickering flames [ A gold trophy with flickering flames [
[

(Click the "Refresh" icon by RESULT above to re-run the animation.)

[