A collection of icons formed by animating 28 pixels around an 11x11 grid
Departure Mono
The nice pixel typeface you may notice around here is Departure Mono, designed by Helena Zhang. I first saw it used on Making Software by Dan Hollick. The font has lots of non-text characters built-in, especially some great ones for box drawings and ASCII art.
I'm using Departure sparingly, and not every page here needs it, but I wanted to thread the pixel motif into a few other places. At first glance, it seemed like we Departure would let me replace my Tabler icons with symbols.
↖ ↗ ↘ ↙ ← ↑ ↓ →
↕ ↰ ↱ ↲ ↳ ↴
× ★ ☆ ♥ ♦ ✦ ✧ √
One issue: all the chevrons are horizontal. I don't want to rotate a single character every time a need a downward-facing one. Time to make some custom icons.
Pixel perfect pixel art
Departure is drawn at an albeit irregular size of 11 pixels, but it scales relatively well. Since characters are pixel-perfect at font-size multiples of 11px, you can use a half-step at 16.5px to make each "pixel" land on a pixel edge on at least one side. This equates to an icon with a 1.5px stroke (like Lucide) and remains decently crisp.
That means we should be able use the trio of 11 / 16.5 / 22px in place of common SVG sizes like 12, 16, 20, and 24px.
For monospacing, the em box of a given character is 8×14, with ascenders or descenders exceeding the 11px bounding box. This gives us some wiggle room for a larger 14px set if we need it.
Direction
I landed on two sets of icons with different constraints. I'll explain why in a moment, but for now, let's take a look at each.
- First are static symbolic icons for UI needs, filling in any gaps in Departure Mono. These can use any number of pixels, typically 16 or fewer, and can have open terminals or shapes.
- Next are animated pictorial icons that always use 28 pixels. These all have closed shapes for consistency and can animate from one to another.
ChevronDown
Chevrons
Loader
Shuffle
Redo
Finder
∙ click each to morph
Twenty-eight pixels
The 28-pixel constraint started with the light/dark mode toggle. I wanted to do something I'd seen before: a sun icon rotates into a moon icon and call it a day. With a regular SVG, this works just fine.
We can rig this up with Tabler and Motion:
Tabler + Motion
But how might one accomplish this with pixels? They don't rotate. Elements painted with pixels can appearto rotate, but really it's just other pixels along its path lighting up. Pixels are discrete and don't maintain the kind of illusory continuity you'd get from a rotated path, so having a little rectangle spin into place breaks the metaphor.
The two states needed to use the same number of pixels, and the pixels ought to reshuffle rather than rotate.
Nor do pixels slide diagonally, but we're suspending disbelief on that count. I could have each pixel animate using steps or SMIL, but then the animation would be way too slow and jerky.
So, why 28 pixels? That's just how many pixels it took for the sun to look right, and it's the first one I made. That's all. This could work with any number of pixels, and you don't even need a constant number; unused pixels could combine or dissolve. Speaking of which: the moon only needed 24, so it earned a little star. Good job, buddy.
In reality, each "pixel" is a rectelement with a width and height of 1px. Motion animates each's x and y properties to move it to its new position.
Tabler + Motion
Pixels
It's self-evident that the path-based approach is smoother, but that's not the point:they shouldn't look polished, the icons are intentionally lo-res. They're inherently rough and imperfect. We can add a hint of gracefulness to their rearranging, but that's more a byproduct of easing.
Going overboard
Then I saw this postfrom Benji Taylor about morphing icons with Claude, in which he's animating between 21 different SVGs made from 3 lines each. That got me wondering how many more icons I could squeeze out of 28 pixels.
A lot, it turns out.
(Some of these are duplicates or variants of others, mostly different attempts at getting the shapes right. They're still kicking around to test which animate well.)
Codex to the rescue
Instead of copy-pasting from Figma and manually converting to JSX, I started off having Codex use the Figma MCP to grab frames and populate the SVGs as individual components.
It got clever after a few passes and wrote a tiny createPixelIcon helper function, which instead generates icons from the X/Y coordinates of each 1x1 layer per frame. Icons then become a string it parses from Figma, and the helper renders rect elements inside a shared SVG wrapper.
Morph visualizer(s)
With the second batch of icons created, the next step was to see how they animate. Inspired by Benji's post, Codex and I created a visualizer to experiment further. You can select a sequence of icons to see how pixels match up between states, and test different animations, speeds, etc. Give it a spin below.
v1: Sequence builder
Build a sequence, jump between steps, and play transitions from the preview or footer.
v2: Click-to-morph
Start empty, click an icon to preview it, then click another to morph. Click the active icon again to clear.