Google vs optimised

Optimising SVGs for Web Use — Part 2

If you haven’t then I’d suggest reading part 1 first. In this part 2 I’ll be showing you an example on how to optimise svgs for web use. I’ve explored a couple of other techniques since writing this and you can read about those in part 2½.

I’ve optimised some of the most commonly used social icons, badges and flags here: github.com/larsenwork/web.svg.min


Step 1 — get/draw an icon

I’ll be using the ♿ icon as example: design.google.com/icons/#ic_accessible

Google Material Design icons are already quite optimised but I like a challenge so let’s try taking it even further.

This is what the downloaded svg looks like:

<svg fill=”#000000" height=”24" viewBox=”0 0 24 24" width=”24" xmlns=”http://www.w3.org/2000/svg">
<path d=”M0 0h24v24H0z” fill=”none”/>
<circle cx=”12" cy=”4" r=”2"/>
<path d=”M19 13v-2c-1.54.02–3.09-.75–4.07–1.83l-1.29–1.43c-.17-.19-.38-.34-.61-.45-.01 0-.01-.01-.02-.01H13c-.35-.2-.75-.3–1.19-.26C10.76 7.11 10 8.04 10 9.09V15c0 1.1.9 2 2 2h5v5h2v-5.5c0–1.1-.9–2–2–2h-3v-3.45c1.29 1.07 3.25 1.94 5 1.95zm-6.17 5c-.41 1.16–1.52 2–2.83 2–1.66 0–3–1.34–3–3 0–1.31.84–2.41 2–2.83V12.1c-2.28.46–4 2.48–4 4.9 0 2.76 2.24 5 5 5 2.42 0 4.44–1.72 4.9–4h-2.07z”/>
</svg>

The transparent path covering the entire viewbox is only needed if you want the entire svg to be toggle-able. When I need a toggle-able svg it’s almost always inside an <a> element that I can use instead so I tend to remove those transparent paths.

Step 2 — determine the optimal size for the viewbox

What you want is a grid that is just big enough that your integers don’t distort your shapes.

This takes some experimenting but you almost always need it to be bigger than the supplied one for when you round all values to integers. Since Material Design icons are drawn over a grid it would be foolish to increase the viewbox size to e.g. “0 0 100 100”. Instead we’ll multiply the current grid by 4 to ”0 0 96 96".

Step 3 to 5

I’m using FontForge since it has “round to int” function built in but if you know about other editors that has that too then please let me know.

I’ve screen recorded the FontForge steps since it’s an editor many aren’t familiar with.

Step 3 — load the svg into your editor

Start by creating a new file and press cmd+shift+f. Switch to the “General” tab and change the ascent to 96 and all other values to 0.

Double click on a glyph and press command+shift+i to import the svg to both the Back and Fore layer.

Step 4 — reduce the number of nodes + handles

Switch to the Fore layer and clean it up as much as possible by removing superfluous nodes and handles.

Step 5 — round to int

Select all nodes except for the circle and round to int by pressing command+_

After the rounding you might need to make some adjustments to get a better path fit when comparing to the unmodified Back layer.

When satisfied: Choose “File”, “Export”.

Step 6 — re-add the circle

FontForge converts circles to paths which we don’t want. Open the svg in your vector editor of choice, replace the curved head with a circle and save the svg.

Step 7 — cleanup

The output from your editor is often bloated so we need to clean it up.

<?xml version=”1.0" standalone=”no”?><!DOCTYPE svg PUBLIC “-//W3C//DTD SVG 1.1//EN” “http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width=”100%” height=”100%” viewBox=”0 0 96 96" version=”1.1" xmlns=”http://www.w3.org/2000/svg" xmlns:xlink=”http://www.w3.org/1999/xlink" xml:space=”preserve” style=”fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;”><path d=”M76,52c0,0 -11,1 -20,-8l0,14l12,0c5,0 8,4 8,8l0,22l-8,0l0,-20l-20,0c-5,0 -8,-4 -8,-8l0,-24c0,-8 10,-11 15,-5l5,6c7,8 16,7 16,7l0,8ZM51,72l8,0c0,0 -2,16 -19,16c-11,0 -20,-9 -20,-20c0,-16 16,-19 16,-19l0,8c0,0 -8,3 -8,11c0,7 5,12 12,12c9,0 11,-8 11,-8Z” style=”fill:#000;fill-rule:nonzero;”/><circle cx=”48" cy=”16" r=”8" style=”fill:#000;”/></svg>

Atom with live svg preview

Tools like svgo will get you 90% of the way but I tend to just open the svg in Atom with live svg preview and then removing excess information manually.


The Result

I’ve removed some unneeded commas + spaces after publishing this taking it from 332 to 304 bytes.
Before:  576 bytes 
After : 304 bytes
Saved : 47.2%
BEFORE
<svg fill=”#000000" height=”24" viewBox=”0 0 24 24" width=”24" xmlns=”http://www.w3.org/2000/svg">
<path d=”M0 0h24v24H0z” fill=”none”/>
<circle cx=”12" cy=”4" r=”2"/>
<path d=”M19 13v-2c-1.54.02–3.09-.75–4.07–1.83l-1.29–1.43c-.17-.19-.38-.34-.61-.45-.01 0-.01-.01-.02-.01H13c-.35-.2-.75-.3–1.19-.26C10.76 7.11 10 8.04 10 9.09V15c0 1.1.9 2 2 2h5v5h2v-5.5c0–1.1-.9–2–2–2h-3v-3.45c1.29 1.07 3.25 1.94 5 1.95zm-6.17 5c-.41 1.16–1.52 2–2.83 2–1.66 0–3–1.34–3–3 0–1.31.84–2.41 2–2.83V12.1c-2.28.46–4 2.48–4 4.9 0 2.76 2.24 5 5 5 2.42 0 4.44–1.72 4.9–4h-2.07z”/>
</svg>
AFTER
<svg viewBox=”0 0 96 96">
<circle cx=”48" cy=”16" r=”8"/>
<path d=”M76 52c0 0–11 1–20–8l0 14l12 0c5 0 8 4 8 8l0 22l-8 0l0–20l-20 0c-5 0–8–4–8–8l0–24c0–8 10–11 15–5l5 6c7 8 16 7 16 7l0 8ZM51 72l8 0c0 0–2 16–19 16c-11 0–20–9–20–20c0–16 16–19 16–19l0 8c0 0–8 3–8 11c0 7 5 12 12 12c9 0 11–8 11–8Z”/>
</svg>

Note that I’ve also removed size+fill so remember to set those in your CSS. The xmlns attribute isn’t needed for inline svg.

Before vs after

Another example

I did the same thing using the default accessible icon found here thenounproject.com/term/wheelchair-accessible/133/

Before: 1173 bytes
After : 291 bytes
Saved : 75.2 %
<?xml version=”1.0" encoding=”utf-8"?><!DOCTYPE svg PUBLIC “-//W3C//DTD SVG 1.0//EN” “http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"><svg version=”1.0" xmlns=”http://www.w3.org/2000/svg" xmlns:xlink=”http://www.w3.org/1999/xlink" x=”0px” y=”0px” viewBox=”0 0 87.22 100" enable-background=”new 0 0 87.22 100" xml:space=”preserve”><path fill-rule=”evenodd” clip-rule=”evenodd” d=”M83.624,75.961l-8.729,3.923L62.653,52.71l-1.188–2.625l-2.874,0.039L32.136,50.5
l-0.243–5.646h21.926v-8.781H31.531l-0.77–18.234c3.295–1.436,5.602–4.716,5.602–8.534C36.364,4.16,32.194,0,27.056,0
c-5.148,0–9.312,4.16–9.312,9.305c0,3.25,1.675,6.113,4.201,7.767l1.604,38.063l0.171,4.263l4.271–0.058l27.842–0.395l12.862,28.526
l1.803,3.999l3.988–1.789l12.733–5.717L83.624,75.961L83.624,75.961L83.624,75.961z”></path><path fill-rule=”evenodd” clip-rule=”evenodd” d=”M56.361,73.589l-1.18–0.279c-2.579,10.545–11.988,17.91–22.86,17.91
c-12.984,0–23.542–10.555–23.542–23.534c0–7.993,4.007–15.276,10.568–19.615l-1.112–7.623l-0.77–1.477C6.69,44.55,0,55.551,0,67.686
C0,85.499,14.5,100,32.321,100c12.268,0,23.156–6.833,28.63–17.32L56.361,73.589L56.361,73.589L56.361,73.589z”></path></svg>
<svg viewBox=”0 0 200 200"><path d=”M113 147l9 18c-11 21–32 35–57 35–36 0–65–29–65–65 0–24 13–46 35–57l1 3 2 16c-12 9–20 22–20 38 0 26 21 47 47 47 22 0 40–15 45–36zm54 5l7 16–33 15–29–65–65 1–3–84c-5–3–9–10–9–16C35 9 43 0 54 0s19 9 19 19c0 6–4 14–11 17l1 36h45v18H64v11l59–1 27 60z”/></svg>
Before vs after

PS

There are some more stuff you can do and I’ve written about it in part 2½. I’ve optimised the most commonly used social media icons here: github.com/larsenwork/social.svg.min