Favicon complete!
I’ve successfully recreated my favicon from scratch! It took me less time than I expected. I was able to replicate the original PNG very, very closely.
The original PNG was 50 kB, and the new SVG (after optimizations) is 4.44 kB. It gzips down to 1.64 kB. A pretty significant improvement!
I’d like to make a full-on blog post about the process. I started by trying to autoconvert my PNG into an SVG using an online converter tool, but soon realized such tools have huge limitations. SVGs are very inefficient file formats, and the only reason they manage to be more compact than files from the (comparably more optimized) landscape of raster image file types is because you’re working with an image that can be described in a couple of elegant curves. Autoconverters seem quite bad at identifying what the most essential curves should be. The SVG I created using this tool was 54 kB, larger than my original 50 kB PNG. The quality was worse, too. The autoconversion process produced strange artifacts, extraneous lines, and visual noise.
I then attempted to manually edit that autoconverted SVG, hoping to remove lots of the wasted file space and clean up the visual noise. This turned out to be an impossible task, because the autoconverter had no concept of what kinds of discrete colored regions in the PNG should correspond to discrete colored regions in the SVG, and so it had created a hodge-podge of highly unusual SVG curves that somehow in aggregate produced a coherent visual image, but could not easily be hand-modified without completely destroying the visual image. It was like it was trying to create an anti-modular SVG.
So I swapped to making an SVG favicon by hand, from scratch, by tracing over the original PNG as a template. I made the favicon 512 x 512 pixels, reasoning that if I restricted my nodes to always fall on the pixel grid, 512 x 512 would be a sufficient level of granularity to recreate the entire PNG with a high degree of fidelity. Unfortunately, due to how SVGs are structured, making small file sizes kind of requires that you restrict your nodes to fall along the underlying pixel grid. It’s just one of the awful design decisions that went into the SVG file type.
I learned something unfortunate during this process. Affinity Designer 2, the vector graphics editor that I currently use, has an option to snap the vertices of a curve/shape to the pixel grid, but doesn’t have an option to snap the control points of Bezier curves to the pixel grid. This would be an incredibly helpful feature. You could retain the status of a particular Bezier curve vertex as a “smooth” vertex, ensuring that tangent lines at a particular vertex vary continuous with the location along the curve, which normally would require keeping the control points and the vertex they’re attached to along a single 1D line. Then you could make it so that no matter where you put one of the control points along the pixel grid, the other one automatically snaps to the pixel grid too, meaning they don’t fall along a 1D line anymore but remain as close as possible to falling on that 1D line subject to the constraint that all points fall on the pixel grid.
… if the above paragraph is hard to understand, then that’s why I need to convert it into a full blog post.
Suffice it to say, if vector graphics editors had this feature, it would make it way more ergonomic to work with smooth curves while also respecting the constraint that all coordinates fall along the pixel grid. Without this feature, I have to wait until I run the entire exported SVG through SVGO (or SVGOMG, Jake Archibald’s graphical user interface for SVGO, which is a piece of software that secretly keeps the entire internet running) to see whether the resulting SVG, when the control points are rounded to the pixel grid, looks remotely visually appealing.
Anyways! After creating a manual version of the favicon, Affinity Designer 2 outputs an 8.25 kB SVG. That optimizes down to 4.44 kB using SVGO, and gzips down to 1.64 kB.
Here’s the problem; it turns out that my original favicon, which was an image of a person, had a shadow at the bottom of the original PNG. I decided not to include this shadow in the SVG, and thus the resulting SVG was shorter than I’d anticipated. It also floated in an ugly way above the bottom of the bounding box. I had to restrict its dimensions from 512 x 512 to 480 x 480 to compensate for this change, and left 3 pixels of breathing room on the sides of the image, making sure both the top and bottom had the same number of pixels so it didn’t look like it was “floating.”
Weirdly, swapping to the 480 x 480 version didn’t actually change how it displayed on a browser tab (I tested it in Firefox). I suspect that (some?) browsers may automatically scan an SVG favicon to see if there’s any transparent space near the edges, and automatically clip it off so that the SVG remains square but takes up the maximum possible amount of space. It’s an interesting optimization (if it’s true that’s what Firefox is doing!) and I wonder how much compute time it costs them when rendering a favicon.
In the process of realizing that my favicon needed to be changed to 480 x 480 pixels, I learned why I see so many non-standard sized SVG favicons on the internet. It’s because people probably ran into the exact same problem mid-icon-creation that I did, and because they were trying to snap to the pixel grid for space-saving reasons, ended up having to just resize the pixel grid instead of resizing the curves/shapes in the SVG. I never would have realized this is what was happening until I tried it for myself! Though I’m unsure my explanation for the phenomenon is correct, I don’t know if the average person is thinking about optimization enough to snap everything to the pixel grid, or if there are other reasons they might want to snap to the pixel grid.
I also learned that this “snap to the pixel grid to reduce file size” trick works remarkably well, and that even quite performance-conscious people I know aren’t doing it! matklad is a very performance-aware person, but his favicon is a 5.28 kB SVG, and it displays all the signs of having been optimized by SVGO—it puts all the curves into one single path, and all coordinates are set to 3 decimal places. (I mean, this doesn’t necessarily mean it was optimized by SVGO, but it does seem like weak evidence.) But the one that hasn’t been optimized is pre-emptively snapping all coordinates to the pixel grid. The SVG is 215 x 215 and basically none of the coordinates are snapped to that grid; like I said, they’re using three decimal places!
matklad’s SVG is far simpler than mine—it’s just a single letter M—but mine has a smaller file size simply because it restrains itself to the pixel grid. It’s such a good optimization technique! More people should use it!