Technical

Pixels please: scaling images in CSS

by on 26th March 2015

Until now, web developers have had very little control over how browsers render images when they’re scaled.

Take Mario, for instance:

This is a 24×32 pixel image, scaled up using Photoshop to preserve its crisp, blocky composition. It’s a great example of the low-res pixel art that’s long been a staple of retro and indie games. Let’s see how our image is rendered in the browser when scaled up using CSS:

mario-small

As you can see, the effect is suboptimal – the browser’s default scaling algorithm (usually bi-linear interpolation) has applied anti-aliasing to the image. Whilst good for the majority of purposes, this type of scaling sacrifices the detail in our pixel art. Is there a way to tell the browser to use nearest neighbour interpolation, as we used in Photoshop, to preserve the pixelation of our Mario?

The CSS Image Rendering Property

The image-rendering CSS property aims to do just this. The property can be applied to CSS background images, inline images, and canvas elements.

You can see the effect in the CodePen below. On the left, the image is rendered as per default behaviour, with the property value set to auto. On the right, we’ve applied our preferred scaling method:

See the Pen wBNxvQ by Tom Bennet (@tombennet) on CodePen.

Browser Support

As with all cutting-edge features, there is a degree of inconsistency between browsers. Firefox supports the crisp-edges value with the -moz prefix, in both desktop and mobile versions. Safari now supports the same value, but with the -webkit prefix, on both desktop and mobile. Opera, which is of course Chromium-based, supports the unprefixed value pixelated, much like the most recent version of Google Chrome.

Finally, whilst Internet Explorer does not support image-rendering, we can achieve the same effect by using a non-standard property that has been knocking around since IE 7. By applying -ms-interpolation-mode: nearest-neighbor to the image, our pixel art will remain pleasingly blocky.

The appropriate combination of properties and prefixes are provided below, including some non-standard legacy values supported in older browsers. I’ve also put them into a SCSS mixin for convenience, which is available here as a gist.


@mixin pixelated {
-ms-interpolation-mode: nearest-neighbor; // IE 7+ (non-standard property)
image-rendering: -webkit-optimize-contrast; // Safari 6, UC Browser 9.9
image-rendering: -webkit-crisp-edges; // Safari 7+
image-rendering: -moz-crisp-edges; // Firefox 3.6+
image-rendering: -o-crisp-edges; // Opera 12
image-rendering: pixelated; // Chrome 41+ and Opera 26+
}

If the current browser support is a serious obstacle for you, there are workarounds and alternatives: you can manually resize and export at a higher resolution using your preferred graphics program, or you can upscale using canvas with the imageSmoothingEnabled property. But that’s a tutorial for another time.

I hope this short guide has been useful. Let me know your thoughts on Twitter – I’m @tomcbennet. Hat tip to Smashing Conference London, which prompted me to explore this subject. Thanks for reading, and may all your pixel art stay gloriously blocky.

Mario Fan Art – Credit

Like this? Sign up to read more

Responses

  1. I had no idea this existed – really, really interesting stuff Tom. I’m glad there’s a way to tackle image scaling – I’m constantly trying to screengrab to the same px width I plan to display in my blog posts so as not to spoil the image. Thank you!

  2. Good tips and very useful guide.

  3. Great. Now my space invaders theme will finally be on point!

  4. Great guide, found it very useful for small images.

  5. Sorry, I’m such a CSS noob– how do I get this to work for a background-image? Thanks dude.

  6. Hi Jack!

    You can apply the image-rendering property directly to the element with the background-image. So for example, if you wanted to apply pixelated rendering to the background image of a div with the class ‘foo’, you would use the following CSS:

    .foo {
    background-image: url('http://36bvmt283fg61unuud3h7qua-wpengine.netdna-ssl.com/wp-content/uploads/2015/03/mario-tiny.png');
    background-size: 100%;
    image-rendering: pixelated;
    }

    For optimal cross-browser compatibility, don’t forget to use the appropriate combination of values and prefixes (see my gist).

Comments are closed.