In this article I will show how to combine the advantages of JPG's high compression rate with the flexibility of having alpha channels like we use them in PNG. Open the demo to see this technique in action, a semi transparent JPG in front of HTML text:

Demo of JPG with alpha channel.

As all modern browsers support images with alpha channels1 it has become fairly easy for designers to place semi transparent images in front of irregular backgrounds.

The weapon of choice will be PNG as it is the only widespread image format that supports alpha channels (WEBP is a promising alternative, but currently only supported in Chrome and Android)[Update As Mathias Bynens correctly pointed out in the comments: Opera supports WebP too].

But PNG has its drawbacks. It comes in 2 flavours: a lossless compressed image with 24bit true colors. Images in this format look superb, but can quickly grow HUGE.

32bit PNG.

And then there's the 8bit (256 color) PNG with alpha channel. When exported cleverly with the right color palette and the right amount of dithering it can be rather small, but most of the time a loss in image quality because of the limited color palette is unavoidable. Look closely at the color gradients in this example:

8bit PNG.

Wouldn't it be great if we could get the high compression and rich color depth of the JPG image format together with alpha channels? Actually, with the help of SVG, this is a breeze.

Open demo of JPG with alpha channel.

This demo contains a JPEG that is wrapped into an SVG. The alpha channel then is created via the SVG "mask" element. The SVG and the JPG (compressed at 50%) combined weigh in at 26kb. The same motive exported into a 8bit PNG will be 32kb. Exported into a 32bit PNG it will be whopping 120kb.

Creating the SVG wrapped alpha JPG:

1. Preparing the JPG

Here's how to prepare the JPG:

2. Creating the SVG:

Fire up your editor and create a new SVG. We will put the JPG into a symbol element, so we need only 1 reference to the JPG that we then can use across our document:

<symbol id="img_corona"> <image width="840" height="400" xlink:href="corona.jpg" /> </symbol>

We define a mask. As we only want to have the "alpha part" of our image placed exactly above the "image part",the JPG must be positioned 50% to the left:

<mask id="mask_corona"> <use xlink:href="#img_corona" width="840" height="400" x="-420" y="0" style="overflow:hidden;"/> </mask>

Almost there. Let's put a reference to the symbol containing the image inline. And now we will cut off the right part of the image that contains the "alpha-channel":

<use id="corona" xlink:href="#img_corona" width="420" height="400" style="overflow:hidden;"/>

Last thing to do: apply our "alpha channel" to the image with CSS. Here's the code in one piece: <svg version="1.1"; preserveAspectRatio="none"; xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"> <style type="text/css"> <![CDATA[ #corona{ mask :url(#mask_corona); } ]]> </style> <symbol id="img_corona"> <image width="840" height="400" xlink:href="corona.jpg" /> </symbol> <mask id="mask_corona"> <use xlink:href="#img_corona" width="840" height="400" x="-420" y="0" style="overflow:hidden;"/> </mask> <use id="corona" xlink:href="#img_corona" width="420" height="400" style="overflow:hidden;"/> </svg>

I hope I showed that this is can be an elegant method to get highly compressed images with alpha channels. This method can be used in every browser that supports SVG which means browser support is pretty good.

If you want to check out this example you can play with this pen or check out my github repo with my SVG experiments.

1. To put it simple, an alpha channel is an 8bit grayscale image within an image. The darker a certain pixel in an alpha channel, the less opaque the pixels of our image will be at this coordinates. back

22 Responses to “Applying Alpha Channels to JPGs”

  1. Comment by kl 08/01/2014

    Your PNG8 is posterized only because you’ve used Photoshop, which can’t support full-fidelity palettes.

    If you use any modern tool, e.g. TinyPNG, ImageAlpha the PNG8 image will be quite smooth:

    Here’s PNG8+alpha: http://i.imgur.com/LThEifD.png

  2. Comment by Dirk 08/01/2014

    Hi kl, thanks for the comment. Actually I’m using Fireworks for exporting PNG graphics (my Photoshop 4 can’t do 8bit PNG with alpha anyway). I like to have the manual control Fireworks gives me over the color palette and the amount of dithering. I must admit that I applied a 50% dithering, not 100% to emphasize the effect in the image. I know that 100% will look better as the posterization will be not so intense but it will still be there (if you look close you can see the effect it in your example too). Anyway PNG has its use, but for some motives JPG is the better option.

  3. Comment by James Collins 08/01/2014

    Very clever

  4. Comment by Rikkert Koppes 08/03/2014

    IWould it make sense to include the actual image as base64 data url in the svg? This would yield a self contained svg image. What is the size in that case?

  5. Comment by Joey Hoer 08/04/2014

    Really brilliant idea – I’m sure ImageMagick could be used to extract the mask and JPG from a high fidelity PNG rather easily. The results could then be easily passed through compression tools and reassembled into a single SVG using data URIs. When serving this SVG with gzip compression enabled, I would expect the savings to be quite respectable.

  6. Comment by Dirk 08/04/2014

    Would totally make sense, as long as server side gzip compression is enabled. Size would propably be around 10% bigger, but you’d save a request.

  7. Comment by Gerardo 08/04/2014

    I think css mask won’t work in IE

  8. Comment by KevinB 08/05/2014

    There are lots of dimensions on here – is there a way to replace with percentages so that this could be used in a responsive (fluid) column? Not sure how much/how well SVG supports width=100%, especially when you have so many different widths and the offset going on. Otherwise, an incredible technique, thanks!

  9. Comment by Dirk 08/06/2014

    Nobody is talking about CSS masks. What exactly is your point?

  10. Comment by Dirk 08/06/2014

    You can stretch and squeeze SVG easily with CSS. No problem here :).

  11. Comment by KevinB 08/06/2014

    According to the SVG spec, SVG’s “image” requires both width and height. I’ll play around with your code to see if I can achieve the necessary stretching and squeezing, but using the traditional approach that we do in HTML of “img style=width: 100%” may not work. My goal would be to not have any px dimensions in any part of the code, whether it’s the symbol, mask, or use. (sorry for the repost, the commenting system took out my code notations.

  12. Comment by Mathias Bynens 08/07/2014

    WebP is supported in Opera too, so not just Chrome and Android.

  13. Pingback by Applying Alpha Channels to JPGs | eleqtriq | Au... 08/07/2014

    […] Learn how to reduce page weight by replacing PNGs with alpha channels with JPGs.  […]

  14. Comment by Dirk 08/07/2014

    @KevinB Let me assure you: you can safely use px inside of an SVG. SVGs live in their own coordinate system, that scales with the containing SVG :)

  15. Comment by Dirk 08/07/2014

    @Mathias you’re right, Opera is build upon Blink after all :)

  16. Comment by KevinB 08/08/2014

    @Dirk – Of course! I forgot all about that. With a little help from http://demosthenes.info/blog/744/Make-SVG-Responsive I got it to work as expected. The key was to set a viewbox and leave all the px inside the SVG (widths AND heights) at the necessary values for the native size of the JPG. And then all the scaling happens on the container divs.

  17. Comment by Joey Hoer 08/08/2014

    Concerning the dataURI encoded JPGs, you may be interested in following the Safari bug report for dataURIs in SVGs called by the tag https://bugs.webkit.org/show_bug.cgi?id=99677. It works in every other major browser back to IE9.

  18. Comment by Dirk 08/08/2014

    Hi Joey, I’ve written about this bug and a workaround here. Tested it a few weeks ago and the bugs seems not to exist anymore it seems.

  19. Comment by Marts 08/28/2014

    You are writing width=”430″ in

    Is this a Typo?

    Nifty Trick btw – Kudos!

  20. Comment by Dirk 08/30/2014

    Yup typo. Corrected.