tumblr

On the quest for pixel crispness

As you may know, Tumblr switched from 500-pixel wide photosets to 540 pixels a while ago. I usually don’t complain, but this time they hit me in an area where I put most attention to: pixel crispness.

As you might imagine, pixel art is best displayed at a 1:1 ratio. If the source image is 500px, you should display it at 500px. If you, for example, display it at 540px, the browser will stretch the image to fit 540px and use interpolation (blending of neighboring pixels) to fill the wider/narrower width. OK for photos, not ideal for pixel art.

Tumblr dashboard has been for the longest time 500px wide. To avoid interpolation and preserve the crisp nature of pixel art, I took extra care to upload most images at exactly 500px wide.

Sometimes I’d scale the image to 200% or 300% by hand with nearest-neighbor interpolation (or none as it’s called in GIMP) which also preserves crisp pixels. That’s how I like to present pixel art the most, at x2 scale.

I crop the resized image to 500px if the cropping isn’t too invasive. Otherwise I leave it at bigger than 500px and let the browser downsample it (resize to smaller). Downsampling is less ugly than supersampling (enlarging) so I let this one go. When you click the image to see it fullscreen it is displayed at proper size anyway.

…

Enter October 30th 2014 when Tumblr changed the width of posts to 540px.

Ouch.

All previous posts now get stretched from 500 to 540px.

Alright, I’ll just upload new posts at 540px. No go. Tumblr resizes them down to 500px when uploading, and display at 540px.

I thought I’ve lost the battle.

Luckily, this only applied to the dashboard — my theme can still display the 500px wide photosets. At least I have one more reason to create special pages that combine feature posts and display them side-by-side at 500px, the way they were meant to be, as crisp as it gets!

…

At that point I went on my hiatus and as I returned, apparently Tumblr has made an important change, namely, they now resize images to 540px on upload! YAY!

Problem solved. Almost …

Things look good on the dash now, at least for new posts.

But let’s go back to my theme. Now the 540 images are scaled down to 500. Hmm, not good. Consulting the docs revealed I should choose between 3 contenders for my new photosets:

  • {Photoset}: Embed code for a responsive Photoset that shrinks to fit the container (max. 700-pixels wide).
  • {Photoset-700}: Embed code for a 700-pixel wide photoset.
  • {Photoset-500}: Embed code for a 500-pixel wide photoset.

Why is there no 540 version? Argh.

At this point I embarked on a quest to explore how each of these photosets worked. The 500 one uses the old 500px assets, while the responsive one uses the new 540px assets. This seemed to be exactly what I need! I’ll just use the {Photoset} tag and put it in a 540px wide wrapper.

Easy peasy. Or so I thought.

It turns out that the {Photoset} version is only a resizable version of the {Photoset-700} one. It renders at 700px wide and uses the zoom CSS to scale down to whatever width it needs to fit (in my case to 540px so it uses a factor of 0.771 (540/700)).

You wouldn’t be surprised at this point if I tell you that zoom also uses interpolation. Even though we start with a 540px wide image and through our wrapper force the display size to be 540px wide, the image gets upscaled to 700px first and then downscaled by 0.771 to reach 540px. If you’ve ever enlarged an image and shrunk it back to original size, you’ll know that the original crispness is lost.

What to do at this point? Well, fuck it, let’s just Javascript the hell out of the photoset iframe and manually hack the image widths to display exactly at 540px and exterminate the zoom scaling. Yes, I used Javascript as a verb.

Additionally I set the margins to match the new condensed photoset layout. This makes my technique useful to you, if you are a theme designers, no matter if you care about pixel art (an alternative version is also to use your own photoset layout code using directly the JSON data).

Anyway, here is my code (using jQuery):

// Resize iframe contents to 540px for pixel crispness!
$iframe = $('.html_photoset iframe');
$iframe.ready(function(){
    tryChange = setInterval(function() {
        $photoset = $iframe.contents();
        $rows = $photoset.find('.photoset_row');
        if ($rows.length) {
            clearInterval(tryChange);
        } else {
            return;
        }
        $photoset.find('.photoset').css('zoom', '');
        $photoset.find('.photoset_row').width('540px').each(function(){
            height = $(this).height();
            $(this).height(height/700*540);
        }).css('margin-top', '4px').eq(0).css('margin-top', '0px');
        $photoset.find('.photoset_row_1 img').width('540px');
        $photoset.find('.photoset_row_2 img').width('268px');
        $photoset.find('.photoset_row_3 img').width('177px').eq(1).width('178px');
        $photoset.find('.photoset_row_2 a, .photoset_row_3 a').css('margin-left', '4px').eq(0).css('margin-left', '0px');
    }, 100);
});

You’ll see how it works in tomorrow’s new PixelJoint feature.

And THAT is how much I care about pixel crispness on my Tumblr blog.