Fixing blurry SVG rendering

Problem

SVG rendering doesn’t always produce a sharp image. To output a vector graphic as pixels on a display, browsers need to rasterize the image, sometimes not resulting in the level of quality you would expect from an SVG, which theoretically supports scaling to arbitrary sizes.

Screenshot of SVG rendered poorly
Figure 1: PNG capture of blurry SVG rendered by a browser.

Solution

The simplest solution I’ve implemented to solve this blurry SVG issue is instructing the browser to rasterize the SVG at a larger size, then scale down the output using a CSS transform. After transforming the element, use a negative margin to make it fill the same space as before. (This is needed because CSS transforms don’t affect an element’s impact on its surroundings.) In other words, render at double the intended size, then scale down to half the rendered size.

The result is an SVG at exactly the size you want and free from SVG rendering blur. Although it’s theoretically not ideal to upscale the image rendering and then downscale the output on the page, I haven’t found any real-world downside for this SVG sharpening technique in use cases I’ve encountered.

Screenshot of SVG rendered sharply
Figure 2: PNG capture of sharpened SVG rendering with CSS scaling solution applied.

I’ve put together a CodePen demonstration of the CSS required for this solution: view solution. This particular example is a scenario where Internet Explorer 11 struggles, any where the downscaling solution produces a sharp image. Other browsers may experience similar issues which such a workaround could be used for.

.before {
    display: inline-block;
    height: 2.62em;
    width: 2.62em;
}

.after {
    display: inline-block;
    margin: -1.31em; /* .25x desired size */
    height: 5.24em;  /* 2x desired size */
    width: 5.24em;   /* 2x desired size */
    transform: scale(.5);
}

.before, .after {
    background-size: 100%;
    background-image: url('...');
    background-repeat: no-repeat;
}