Polishing the Lens on Our Screen Capture Service

wrote this on in , . 30 reactions

A few years back, we made it possible for users of our products, such as Bounce, to capture website screens with a URL. Since then, we tried several different services over the years, trying to find a solution that worked. Some were OK, but that wasn't good enough. A screen capture has to accurately represent what is actually present, and our current service has become unreliable. So we recently pushed to find something that snagged accurate screenshots.

Here's what happens when a URL is entered: screenshot URLs are sent to our server, which then takes a screenshot of the site and sends it back to our apps. Our current implementation uses wkhtmltoimage to capture screenshots, but that's a bit defunct now.

While looking for new solutions we learned that there had been a bit more activity in this area than when we started a few years ago — but not much. However we didn't just implement the first thing we found. We tried several different solutions before selecting webkit2png in the end. Here's the steps we took:

You Can't Just Implement an Alternative Solution

When looking for screen capture quality comparisons, there wasn't a lot of information out there. What little was there wasn't enough, so we had to dig a bit and ultimately came up with the following screenshot capture alternatives:

But we couldn't just jump into using an alternate solution. We had to know if it would actually be the one we needed. Take PhantomJS, which is the service we kept hearing about. It's used for automated browser testing, but also does a great job capturing screenshots. So we took it for a spin. Using the following script, we were able to capture screenshots from the command line:

var system = require('system');
var url = system.args[1];
var page = require('webpage').create();
page.viewportSize = { width: 1024, height: 800 };
page.open(url, function () {
  }, 1000);

Running this is trivial enough: just run phantomjs ~/capture.js http://zurb.com. The end result was a drastic improvement over wkhtmltoimage, but some of the fonts were still incorrect. Swipe the image to see the after (note how off the fonts are):

screenshot with wkhtmltoimagescreenshot with phantom js

Above left: Screen shot with wkhtmltoimage. Above right: Screen shot with phantomjs.

However, this was still a drastic improvement. We decided to test Phantom on our Ubuntu production system. After installing some basic fonts on the system (read specifically what we installed) we ended up with an even worse screen capture. Swipe the image to see the after:

screenshot with phantom jsscreenshot with phantom js on ubuntu

Above left: phantomjs screenshot on a test server. Above right: phantomjs screenshot on a production server.

By this point we were still happy with the overall image improvements, but wanted to make sure we explored other alternatives. We decided to try out a hosted service called called Url2Png. It worked without hiccups, but ultimately decided against it for the following reasons:

  • Price: On average, we're seeing close to 10K screen captures a month. The cost would be quite a setback, especially when we could host something ourselves to it'd hurt the wallet less.
  • Beta: The service is still considered beta. We wouldn't want to rewrite all of our applications that use this service only to see the service suddenly become discontinued.

It's Always the Last Place You Look

Finally, we decided to check out webkit2png. Webkit2png is a simple command line Python script that captures web pages with WebKit bindings. Because of this simplicity it's able to provide accurate screenshots with relatively few dependencies. The only caveat is that it only runs on Mac OS X. It got us thinking though, why not host the capture service in a Mac environment? This would avoid all the funky screen capture issues that we had been running into. We purchased a MacMini server from MacMiniVault to do this. Now the screen captures look identical to what they looked like on our development systems. Check it out below, can you tell the difference?

original screenshotscreenshot with webkit2png on a mac mini server

Above left: Screenshot from Google Chrome on a MacBook Pro. Above right: webkit2png screenshot on a Mac Mini server.

Going the Distance

The PNG images created by webkit2png were significantly larger than the previous JPEG images generated by wkthmltoimage. We could have easily converted these PNG images to JPEG but we wanted to keep the files as PNG going forward for the following reasons:

JPEG images tend to become grainy while PNG images do not Color samples found in PNG images appeared to be more accurate as compared to JPEG counterparts (at least without further color profile configuration).

Luckily, there was a tool out there called pngquant which claimed up to 80% compression on PNG images. We tested on a screen capture of our website. Sure enough, with pngquant the image went from 1141KB to 278KB, a 75% compression very similar to what JPEG conversion would get us. Swipe to see the difference between the original (the first image) and compressed PNG images (the second):

screenshot with webkit2png on a mac mini serverscreenshot with webkit2png on a mac mini server, compressed with pngquant

Above left: Screenshot without compression. Above right: Compressed screenshot.

Here's Our Conclusion

Our users depend on accurate screenshots to give and receive contextual feedback. But we learned that obtaining accurate screenshots is still much more difficult than it should be. An unlikely solution allowed us to go beyond "good enough" and implement something that worked.

By the way, the image swipe used in this post is completely responsive and we'll soon be releasing it on our Playground.

Swing and a Miss
You're Design Thinking Too Much
Design pot
Design or Get Off the Pot