When last we spoke we had just finished discussing the merits of delivering assets as SVGs instead of PNGs, or other raster formats. We found that SVGs offered greater file size savings and compressibility while simultaneously offering superior image quality. We also saw that we could optimize SVGs to save on file size even more.

So how does one optimize an SVG?

Great question! I’m glad you asked. You see drawing apps tend to use the general purpose and quite versatile <path> tag to create objects in an SVG image. By using more specific tags we can cut down on the amount of text used to describe an object thereby saving on file size. We can also define objects in the <defs> tag, and recall them for use later via the <use> tag. Finally, for objects which repeat, we can use <pattern> to create a repeating pattern. Patterns are saved in <defs> and used in an object’s fill attribute, as we’ll see below.

Background

Instead of explaining every step in minute detail I’ll show you the general steps used to optimize our owl image. For each type of optimization performed I’ll take an example from the image and walk through it. Hopefully there’ll be enough info here to help you:

understand what happened with the parts of the image left unexplained play around with the original image, working along with the sections to aid in your understanding of what we’re doing.

We’ll start with the chevron background. It is actually the <g id="chevron"> group repeated 12 times. The first three are shown below.

And here’s the original markup that gave us the image above.

To create this background:

I define a group of rectangles at a 90 degree angles to each other. Next I rotate the group by 45 degrees around the center point of the group. Finally, I shift the whole thing up a bit because I want to.

Above you see the results of steps 1, 2, and 3. Below is the code which does what was explained above. Our right-angle <rect>s are defined in <g id="chevron"> .

Once the shape is defined I take the entire group and rotate it by 45 degrees. The rotate() function rotates an object about the x and y coordinates given. Otherwise it rotates an object about (0 0) .

What I wanted to do after rotation was to shift the group to the right by 17px. If you try to edit the value in the browser’s inspector you will quickly realize that this is not actually happening. The code seems to result in movement of the x and y coordinates. That’s because transform parameters are applied from left to right, in the order that they are written. This means that transform="rotate(-45 101.3705 90.104) translate(17 0)" will rotate our group around (101.3705 90.104) and then move this rotated object 17px along the x axis. But since our x-axis is rotated by 45 degrees, it seems to be moving in the x and y direction.

Hopefully it’s clear why I should have done transform=" translate(17 0) rotate(-45 101.3705 90.104)" instead (move, then rotate) to achieve horizontal movement along the original x-axis. Why keep the mistake then? Makes for a great teachable moment I think. Once I had the chevron group in place I created a pattern for it as shown above.

The SVG specification document actually does a good job of explaining the different pattern parameters. The pattern contains a reference to our rotated chevron via the <use> tag. Meanwhile a <rect> the size of the canvas was created and its fill value — instead of being a color, or a gradient — is the id of our pattern.

Eyes

Let’s move on to the eyes. They are simple circles. Height = width with a radius that’s half of the diameter (which is the width).

Here’s the original svg code for that

What we have above is a group of circles which make up the eye grouped with a <g> tag. What’s the <g> ’s purpose? To group things. We also have an id on the <g> to help identify it. So when this was originally made in iDraw I made an eye, grouped it, named the group, and then copied it to create the other eye. Then I drug the copied eye over to its new position. Copying the original eye duplicated the id attribute sadly; a big no-no for HTML/XML documents, where all ids must be unique. In the case of an SVG (which is an xml document) if we target that id with javascript weird things will happen. Part of the optimization process then, will include removing these duplicate ids.

Here is the optimized code:

Quite a difference isn’t it. You’ll notice it’s much more efficient to define a circle using <circle> than to use a path.

Ears and Nose

Next came the ears and the nose. These three objects are all the same since the ears are just rotated and scaled versions of the nose.

Below you’ll notice there are three separate path objects.

Instead of keeping these three we define one in <defs> and reference it via <use> and the xlink:href attribute. One of the ears requires a rotation of the <use>. The second ear references the first ear and translates it, then flips it horizontally.

Wings

Here are the wings we’ll be flying high with today.

Now this one’s interesting. Since we only have two hands (wings) we don’t save a whole lot by using <defs> and <use> tags here. As a matter of fact, use of the <defs> tag isn’t really necessary. Why do it then? Just to keep the markup clean. And speaking of the markup, here it is:

Our optimized code takes the second hand — id="hand-l" above — and sticks it in a <defs> as id="hand" . Then we use it later to make our left and (horizontally flipped) right hands. If you look below you’ll see transform="scale(-1 1) translate(-616 0)" . We use scale to mirror objects by giving it negative values. The scale(-1 1) changes the x-axis values, flipping our wings horizontally.

Using this function changes the x coordinate values and makes them negative. Since the x values become negative, our wing will be outside the boundary of our canvas. We compensate for this by using translate to move our wing to its new location within the bounds of our canvas.

Eventually we end up with a file that, as you’ll see below, brings quite some savings from the original while looking just as good.

Original Optimized

Conclusion

After going through all this trouble a very good question to ask is, Was it worth it? My answer is: it depends.

If you’re goal is to animate SVGs later, your best bet is actually to leave your <path> s intact .

s intact . If you’re just using them as static images I’d recommend getting those file sizes down as much as possible. You can see from the table below that optimization of our owl image gives us ~67% savings in both our normal and compressed SVG.

Table 1. File size comparison between SVG owl image

SVG Compressed SVG Original file size 16K 5.6K Optimized file size 5.3K 1.8K % savings of optimization 66.9% 67.9%

Comments? @reply to @opinion8d_logic on twitter.

Addendum

Optimizing by hand, while great for learning, does become tedious after a while. It sure would be great if there were some thing, some tool we could use to help us. And lo’, there is just such a tool by the name of SVGOMG. This is actually based on the node.js app SVGO, but what makes SVGOMG much nicer is that we get a live preview of the changes it proposes.

While SVGO won’t replace redundant items with <pattern> or use <circle> and <rect> where possible, it still achieves considerable file size savings. Given that, SVGO and SVGOMG are promising tools for developers in their constant battle against slow-loading sites and heavy apps.

Combined, manual and programmatic optimization of SVGs promise considerable file size savings. Moreso than is attainable by either technique individually. At least one of these techniques should be used before declaring a project production-ready.