tobek
tobek

Reputation: 4539

Small unscaled SVG rendering blurry and wrong in all browsers

I have an SVG that renders completely awfully in all browsers.

There's lots of questions here about fuzzy SVG rendering but all the questions I saw were specific to certain browsers, or involved scaling, or applying transforms, or using SVG filters, or using as a background-image, etc.

This is not upscaled or downscaled, and there are no filters or anything funky going on, just a single path in an <svg> element. It's 24x24px. When I zoom in it looks fine, and it looks great on retina screens.

On the left is the image in Sketch (looks equally good in Illustrator etc). On the right is the image in Chrome, Safari, and Firefox on OS X. Of the 3 icons, the top is normal, middle has transform: rotate(0.1deg) (saw some people had some success with that), and on the bottom it has shape-rendering: crispEdges. They're all cruddy:

crappy SVG rendering

Looked bad to varying degrees on a Windows machine, including in IE.

Here's the SVG:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
    <g id="Production" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
        <g id="Desktop-Web-Feed" sketch:type="MSArtboardGroup" transform="translate(-110.000000, -504.000000)">
            <g id="Share" sketch:type="MSLayerGroup" transform="translate(26.000000, 504.000000)">
                <g id="Facebook" transform="translate(84.000000, 0.000000)" sketch:type="MSShapeGroup">
                    <g id="icon-web-feed-fb">
                        <rect id="Bounds" opacity="0.30626166" x="0" y="0" width="24" height="24"></rect>
                        <path d="M10,19 L10,12.9000244 L8,12.9000244 L8,10.9108276 L10,10.9108276 L10.0697378,8.28793345 C10.0697378,6.15850696 11.3218994,5 13.1515237,5 C14.02764,5 14.7807229,5.06596481 15,5.09688581 L15,7.32319812 L13.7299787,7.32319812 C12.7377746,7.32319812 12,7.70703125 12,8.53530148 L12,10.9108276 L14.8771973,10.9108276 L14.6080794,12.9000244 L12,12.9000244 L12,19 L9.1929245,19" id="Shape" stroke="#A1A1A1"></path>
                    </g>
                </g>
            </g>
        </g>
    </g>
</svg>

Here are some things I tried that didn't work:

Strangely, the basic layout of the SVG is simply different in the browser - there should be 2 pixels of space inside the letter, but even with crisp anti-aliasing there's only 1 pixel of space in all browsers.

Is there any way to improve this? Is it something to do with this particular SVG? Or is it just that browsers are really bad at rendering small SVGs?

Upvotes: 4

Views: 4713

Answers (2)

Paul LeBeau
Paul LeBeau

Reputation: 101810

The results when I try your SVG are not as bad as your screenshot. So I'm not sure why that is. Sketch looks like it may be bumping the geometry to line up with pixel boundaries - not unlike font renderers.

You can significantly improve the look of your icon by thinking about how the outlines of your shape are with respect to pixel boundaries.

If you blow up your icon a bit, and draw grid lines corresponding to a 24x24 pixel rendering, you will see what I mean:

var grid = document.getElementById("grid");
for (var i=1; i<24; i++) {
  var line = document.createElementNS("http://www.w3.org/2000/svg", "line");
  line.setAttribute("x1", 0);
  line.setAttribute("y1", i);
  line.setAttribute("x2", 24);
  line.setAttribute("y2", i);
  grid.appendChild(line);
  line = document.createElementNS("http://www.w3.org/2000/svg", "line");
  line.setAttribute("x1", i);
  line.setAttribute("y1", 0);
  line.setAttribute("x2", i);
  line.setAttribute("y2", 24);
  grid.appendChild(line);
}
<svg id="mysvg" width="240px" height="240px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
    <g id="Production" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
        <g id="Desktop-Web-Feed" sketch:type="MSArtboardGroup" transform="translate(-110.000000, -504.000000)">
            <g id="Share" sketch:type="MSLayerGroup" transform="translate(26.000000, 504.000000)">
                <g id="Facebook" transform="translate(84.000000, 0.000000)" sketch:type="MSShapeGroup">
                    <g id="icon-web-feed-fb">
                        <rect id="Bounds" opacity="0.30626166" x="0" y="0" width="24" height="24"></rect>
                        <path d="M10,19 L10,12.9000244 L8,12.9000244 L8,10.9108276 L10,10.9108276 L10.0697378,8.28793345 C10.0697378,6.15850696 11.3218994,5 13.1515237,5 C14.02764,5 14.7807229,5.06596481 15,5.09688581 L15,7.32319812 L13.7299787,7.32319812 C12.7377746,7.32319812 12,7.70703125 12,8.53530148 L12,10.9108276 L14.8771973,10.9108276 L14.6080794,12.9000244 L12,12.9000244 L12,19 L9.1929245,19" id="Shape" stroke="#A1A1A1"></path>
                    </g>
                </g>
            </g>
        </g>
    </g>
    <g id="grid" fill="none" stroke="#ccd" stroke-width="0.1">
    </g>
</svg>

You can see that the lines of your shape are falling exactly halfway between the final pixels. That will result in the worst possible antialiasing. You should consider tweaking your icon design to move the lines so they correspond with pixels better. Turn on grid lines in Sketch and set them up to show the pixel boundaries. And work from there.

Upvotes: 5

Mathew Eis
Mathew Eis

Reputation: 290

Unfortunately you probably aren't going to get a better result at that size. Mostly, it is the size that you're trying to render it at, although the color doesn't help much either - with the low contrast the sub pixel rendering is simply not possible to to well. Although the pixel size is 24x24, you're left with about 10 pixels to render the SVG in. Not many icons are going to look very good at 10px.

One easy way to tell if the issue is with the SVG renderer is to render it at double-size (or larger), then scale it down using a bitmap resampling algorithms (most standard good quality image editors).

Try converting it on iConvert Icons SVG icon converter. First upload the SVG; it will redraw at each size - notice how below 32px it starts getting a bit blurry.

Then, grab the 256x256 image, which looks nice and sharp, and run that through the icon converter. Notice how the results look almost exactly the same - despite the fact that the source is an image, not some downscaled SVG.

With that said, I made a few minor changes to the code (removed some unnecessary transformations, darkened the icon, and made it fill instead of stroke), and I think it helps improve the contrast (and thus, the "sharpness") at small sizes:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
    <g id="Production" stroke="none" stroke-width="0.8" fill="none" sketch:type="MSPage">
        <path d="M10,19 L10,12.9000244 L8,12.9000244 L8,10.9108276 L10,10.9108276 L10.0697378,8.28793345 C10.0697378,6.15850696 11.3218994,5 13.1515237,5 C14.02764,5 14.7807229,5.06596481 15,5.09688581 L15,7.32319812 L13.7299787,7.32319812 C12.7377746,7.32319812 12,7.70703125 12,8.53530148 L12,10.9108276 L14.8771973,10.9108276 L14.6080794,12.9000244 L12,12.9000244 L12,19 L9.1929245,19" id="Shape" fill="#222222"></path>
    </g>
</svg>

Upvotes: 1

Related Questions