iD depends on SVG for drawing map features, displaying tiles, and a model upon which to build complex interactions.

In the process of building it, we’ve learned a lot about SVG’s performance equation - and it’s time to share some of this. A lot of this is taken from, a sort of developer-journal which has grown over the last few weeks.

note that the examples in this post are designed to run around ~30fps in Google Chrome on a fast computer. They’re made to push the limits, so may crash slower browsers on slower machines.

First Steps with SVG

We’re generally using d3 for graphics in iD. It’s a very, very light abstraction layer over SVG - it eliminates the need for manual namespacing, gives a jQuery-like chainable attr setter, and so on.

SVG elements are elements on-page just like HTML elements, with a few differences:

  • There’s no z-index - order in-page is the rendering model
  • SVG has its own transform property, which is different and has difference performance implications than CSS’s transform property.
  • Many CSS styles work with SVG elements, but many do not. Some things we’d think of as styling variables, like the radii of circles, are actually expressed as attributes rather than style rules.
  • SVG has a powerful and possibly awful system of doing inter-references: a <defs> element for definitions which are used by other elements by xlink:ref attributes. For instance, you need to define a <path> in <defs> in order to reference it from a textPath element in your main drawing canvas if you wish to draw text on a path.


There are a few basic goals of any performance tuning task:

SVG’s special moves: g and defs

The basic structure of the map canvas for iD is as follows:

div (supersurface)
  svg (surface)
      path (textPath data)
    g (tilegroup)
    r (vector root)
      g (fill, casing, stroke, text, hit, temp)
        (path, g, marker, etc)

Besides just having more shapes, SVG has different concepts - one is the <g> element, that represents a group. The groups can be very useful, since they can be transformed with the transform attribute: for instance, markers are set up like

<g transform='translate(10, 20)'>
    <image />
    <circle />

SVG’s defs element is another oddball: it defines content that doesn’t show up on screen, but can be used via reference: I’ll mention this later on with textPath as a example. But another usage of defs for this project is making a rect element which is used to clip the vector and tile layers.

CSS Transforms are a big boost - sometimes.

For panning the map, transforms seemed to be the main solution - they allow simple pan behaviors to be carried out on a single element, and in a way that doesn’t require a browser to recalculate individual element positions.

However, early results were not encouraging - CSS transforms, even of the ‘hardware accelerated’ 3D variety - are not accelerated in Google Chrome when used with SVG elements - they have the same performance profile as SVG attribute transforms.

The answer is to transform HTML elements, which does trigger fast code in Google Chrome. In iD we do this by transforming a ‘surface’ element (the SVG parent) of the map instead of the group (<g>) elements that make up the tile surface and vector surface.

(update: these now compensate for different vendor prefixes for the transform CSS property)

Rounded pixels give a boost

Vladimir brought up this one and I didn’t expect it to be a significant bump, since SVG is vectorized, I assumed that it wouldn’t have raster-pixel bumps like Canvas does. However, it is a boost, and not just because of the shorter string length of d attributes - rounding and outputting long fixed-point numbers is also faster than non-rounded values. This is about a 20% improvement in testing.

Markers along a path

One of the features we need in iD is an indication of roads that are one-way, and which way they are going.

SVG has some built-in functionality for this purpose - you can define a marker in a defs element and use it on path elements with marker-mid and so on. Unfortunately, this doesn’t cut it, since OSM paths have an extremely variable number of vertices - some straight lines have hundreds of vertices, some have only a few, and would be very sparsely labeled.

Our current solution to this is to use a textPath with a glyph - a right-facing triangle. Here’s what it looks like in a standalone demo.

First, determine the size of an arrow - how many pixels it occupies from left to right on a path:

var arrow = svg.append('text').text('►');
var alength = arrow.node().getComputedTextLength();

Then getTotalLength lets you get the pixel-length of a path, and you can then use multiplication & letter spacing to distribute just enough markers for the path:

tp.attr('letter-spacing', alength * 2)
    .attr('xlink:href', '#p').text(function() {
        return (new Array(Math.floor(
            path.node().getTotalLength() / alength / 2)))

Update: I noticed that this example is broken in Firefox. This is due to the following (also noted on the example page):

The original version of this failed because Firefox does not support letter-spacing on SVG elements. The second version failed because it has a bug in getComputedTextLength. The third failed because it does not preserve space according to xml:space.

Needless to say, Firefox is not the main target for iD testing because of a buggy and incomplete implementation of SVG relative to WebKit.

Devlogging work on the OpenStreetMap project by the MapBox team.

Much of this work is currently focused on improvements to OpenStreetMap funded by the Knight Foundation

Follow our work here on this blog or subscribe to our Twitter feed. You can subscribe to this blog’s feed or follow us at