May 2013

Dev diary: SVG markup optimizations

2 Over-optimizing SVGs

Previous optimizations were useful only for optimizing consecutive line segments with the same color and thickness. It would be useful to group lines with same thickness and color to groups. Unfortunately in order to do that it is necessary to reorder lines. Reordering might cause different rendering result if lines are overlapping but in many cases order does not matter. Examples of such L-systems are shows in Figure 1. Because of possibility of unpredictable results of this optimization, it is disabled by default and it can be explicitly enabled with overoptimizeSvg settable property of SvgRenderer2D.

The L-systems with no overlapping lines or when order does not matter too much greatly benefit from reordering and file size savings are large. To save even more, there is two-level hierarchy of groups - by color and by line width. It is dynamically resolved whether it is better to have width groups with color sub-groups or vice versa. Implementation uses SVG g tag to group lines.

Lastly, thanks to path element which enables not only to draw ploy-line but also jump to different location, line segments with same width and color are grouped to the same path tag even if they are not continuous line. This gives us even larger savings in L-systems which have many branches.

H-tree (line thickness differs) — Problematic L-systems for path-groupping
(a) H-tree (line thickness differs)
Hexa Gosper curve (color differs) — Problematic L-systems for path-groupping
(b) Hexa Gosper curve (color differs)

2.1 Results

Nice example to demonstrate benefit of described "over-optimization" are L-systems H-tree and rainbow-colored Hexa Gosper curve. SVG code in Code listing 1 shows how H-tree was "over-optimized" without "damaging" the image (code is truncated). The SVG has as many path tags as many different thicknesses there is. The same is true for the color.

Code listing 1: SVG optimized by groupping lines by stroke width.
<?xml version="1.0" standalone="no"?>
<svg xmlns="" viewBox="-319.8 -451 639.6 454" ...>
<g stroke="#000">
<path stroke-width="6" d="M 0 0 L 0 -256 " />
<path stroke-width="5" d="M 0 -256 L -181 -256 M 0 -256 L 181 -256 " />
<path stroke-width="4" d="M -181 -256 L -181 -128 M -181 -256 L -181 -384 ..." />
<path stroke-width="3" d="M -181 -128 L -90.51 -128 M -181 -128 L -271.5 ..." />

Figure 2 shows results of optimization and over-optimization on several examples. The reduction on non-gzipped SVGs is around 60-80%, gzipped SVGs are around 30-50% smaller.

JavaScript is needed for visualization of this chart.
(a) Raw SVG sizes comparison
JavaScript is needed for visualization of this chart.
(b) GZippled SVG sizes comparison
Plant — Some L-systems listed in Figure 2
(a) Plant
Tree — Some L-systems listed in Figure 2
(b) Tree

Notice how over-optimization "messed" up order in Plant L-system in Figure 4. However, in this case it is barely noticable and acceptable. Look at the bottom of the plant, the small branches were in the back and now they are in the front.

Original SVG — Detail of over-optimization side-effect
(a) Original SVG
Over-optimized SVG — Detail of over-optimization side-effect
(b) Over-optimized SVG

2.2 Future work

As an update to SVG optimization topic on July 2014, I realized that there are several tools that do the SVG optimization, for example:

Optimizations described in this article are specific to L-systems but I believe that those tools would do even better job. Those tools would be quite simple to plug to existing pipeline but I don't like to use non-native tools in 100% managed application like Malsys. However, if there ever be a good reason to use them, I can consider it.