Let’s say you have a photographic image that really should be a JPG or WebP, for the best file size and quality. But what if I need transparency too? Don’t I need PNG for that? Won’t that make for either huge file sizes (PNG-24) or weird quality (PNG-8)? Let’s look at another way that ends up best-of-both-worlds.

The goal is to clip myself out of the image, removing the background. My technique for that is usually to use Photoshop and cut a clipping path manually with the Pen tool.

Now I can select the inverse of that clipping path to easily remove the background.
Attempting to save this as a 1200px wide image as PNG-24 out of Photoshop ends up as about a 1MB image!

We could cut that by 75% using PNG-8, but then we 1) get that weird Giffy look (less photographic) and 2) have to pick a matte color for the edges because we aren’t getting nice alpha transparency here, just binary transparency.

Gosh what if we could just use JPG? The quality and file size is way better.

No transparency though.
But wait! Can’t we just clip this thing out? We have clip-path now. Well… yeah. We do have clip-path. It can’t take a path()
, though, and what we’ve created for vector points in Photoshop is path data. It could take a polygon()
though, if we made all the lines straight. That’s probably not ideal (I’m curvy!). Or we could make a <clipPath>
element in some inline SVG and use clip-path: url(#id_of_clipPath);
, which does support a <path>
inside.
There is masking as well, which is another possibility.
Let’s look at a third possibility though: put everything into <svg>
. That made some logical sense to me, so all this stays together and scales together.
The trick is to make two things:
- The JPG
- The clipping
<path>
The JPG is easy enough. Output that right from Photoshop. Optimize.
Now we can set up the SVG. SVG is happy to take a raster graphic. SVG is known for vector graphics, but it’s a very flexible image format.
<svg>
<image xlink:href="/images/chris.jpg" x="0" y="0">
<svg>
To get the path, we export the path we created with the Pen tool over to Illustrator.

Now we have the path over there, and it’s easy to export as SVG:

Now we have the path data we need:

Let’s use that <path>
within a <clipPath>
in the SVG we’ve started. Then also apply that clip path to the <image>
:
<svg viewBox="0 0 921.17 1409.71">
<defs>
<clipPath id="chris-clip">
<path d=" ... " />
</clipPath>
</defs>
<image xlink:href="/images/chris.jpg" clip-path="url(#chris-clip)" x="0" y="0" width="921" height="1409">
<svg>
Tada!

A transparent JPG, essentially.
Peter Hrynkow has written about this as well. He also used SVG, but used an black-and-white PNG image and the
<mask>
element to apply the transparency to a JPG.There’s even a couple of tools to automate that process:
http://quasimondo.com/ZorroSVG/
https://github.com/arenanet/png2svg
We use
png2svg
in our build process for files where it ends up smaller and it’s been great.Clipping is obviously better if you only have transparent/not-transparent, but that’s a relatively uncommon use-case for us in practice.
I posted a similar thing, but with mask, last month: http://codepen.io/estelle/pen/jyGOpG
change the line mask # in this line to see four examples:
Where #Mask4 can be #Mask3, #Mask2 or #Mask1.
Yours looks way better. The mask examples include a rat SVG and a transparent PNG. There are so many options!
What was the final filesize?
Amazing, thank you.
now thats cool.
Cool!!!
(As you asked ;-)
I created plugin for this (and getting transparent images) 4 years ago :)
https://github.com/Sly777/Jquery-Alpha-Image
Sometimes it’s this kind of thing that I might think of but stop myself because it seems hacky… or because if someone else saw it they would think I don’t know what I’m doing. Ultimately there’s nothing wrong with it though is there? It’s out-of-the-box thinking and has a measureable payoff.
Thanks for posting this, now if I use this method I can be all like “see… Chris can do it so why can’t I?”
As I can see on Firefox I don’t get the image.
Maybe the xlink:href is not supported?
Oops! Firefox needs width and height on the
<image>
, which I didn’t know. Adding now.That’s really super creative!
This is awesome, thanks for sharing.
Awesome :D
Love this technique!
In a perfect world, JPEG2000 would work in more than just Safari, and Photoshop would make it easier to export PNG8 images with varying levels of alpha transparency (which tools like ImageAlpha can totally do). In that scenario, you could just use a
<picture>
element with JPEG2000 as preferred and PNG8 as a fallback.But the SVG mask technique gives you all that goodness with much better support (and fewer deviations from a standard image workflow).
This is what I come to CSS-tricks for!
Thanks for sharing =)
For this case i would normally create a transparent png-24 and then reduce file size drastically with a tool like ImageAlpha or PNGyu.
http://nukesaq88.github.io/Pngyu/
Cool trick – much like we’ve needed to do in print design years before most layout apps supported alpha transparency and layers in images.
Too bad we can’t get the major browsers to support (perhaps) better image formats with alpha tranparency like WebP https://developers.google.com/speed/webp/ or even Flif http://flif.info
Great write-up.
ZorroSVG does something similar – http://quasimondo.com/ZorroSVG/.
A quick note on 8-bit PNG: it does support alpha transparency, not limited to the matte color indexed transparency through the “Save For Web” dialogue. Photoshop now can output 8-bit PNG with alpha via “Export As” (though the colors for 8-bit PNG are still indexed so you still get GIF-like dithering and banding in semi-transparent areas). Surprisingly, Fireworks has pretty robust output options for 8-bit PNG with alpha that Photoshop lacks, including granular color control and dither percentage.
Great solution! Might come in handy when TinyPNG doesn’t deliver the wanted small filesize on transparent PNG.
Have you tried http://compresspng.com ?
This guy is bald, which makes it relatively easy – but I think you could also take on the proverbial Afro hairdo with multiple masks combined with different amounts of transparency.
i use the SuperPNG plugin for Photoshop.
You can have 8bit PNGs with alpha transparency then.
the “giffy” look will still remain. Nice trick!
(services like tinyPNG or imagemin also support 8bit alpha channels — actually, only the photoshop “save for web” 8bit png does not support alpha transparency)
Or you can use export PNG as 24 bit and use pngquant to convert image to 8 bit dithered while keeping 8 bit alpha channel. Not that small as JPEG but more compatible (which I have to admit is not a problem these days with SVG).
I guess the main reason to consider this strategy is to reduce bandwidth. I think the caveats should be mentioned as well. Legacy browser support and additional CPU load should be kept in mind when considering this approach.
In your intro, however, you seem to imply that WebP doesn’t support transparency even though it does and is very effective in doing so. Perhaps it’s just unfortunate phrasing, but I thought it was worth mentioning. WebP isn’t the answer to everything either. Browser support is much worse than masking a JPG, but might be more efficient bandwidth-wise for browsers that do.
Either way, having arguments for and against this approach in the article would allow readers to make thoughtful choices instead of blindly using this.
Regardless of the above, it’s a nice application of SVG masks that might solves someone’s problem.
Legacy brwoser support: this is IMHO the BEST part of this all. I
don’t know if the author realized this, but that thing works in Edge + IE11( and probably down to IE9) despite what caniuse says. Because caniuse (when saying “no” to IE and Edge clippath support) means the CSS clip-path, but the linked article for clip-path that caniuse refers to (http://lab.iamvdo.me/css-svg-masks/) mentiones that IE supports “only SVG clip-path on SVG content” which is exactly what this article uses.
Just open this page in IE and scroll down to the footer to see the glory of JPEG-image-masking for everyone..
It’s a cool idea.
I wrote about adapting Peter’s original method to work with srcset:
https://www.jayfreestone.com/journal/masking-bitmaps-with-svg/
Example here:
https://github.com/jayfreestone/svgbitmapmask/blob/master/scripts.js
Another neat trick for transparent background JPGs is mix-blend-mode: screen, which turns any white in the image transparent. Great for things like logos or other images with a white background.
Hi, I have also make a little tool to generate ajpg images in the past… you can find it at http://xperiments.in/png2ajpg/