The feedback I recieved from Reddit about (my hobby project) was encouraging.

If you would like to join the beta program for locidesktop, please leave a comment below…

One of the comments pointed out that although there is a large choice of icons available, there isn't always a clear recognizable image for each site, and it would be nice if locidesktop would use ‘favicons’. I had considered using favicons previously, but rejected the idea because they are just 16x16 pixels in size, and I wanted to use large images for icons.

I didn't want blurry scaled icons either, and I may have abandoned the idea if a Reddit user hadn't pointed me at this which recommended embracing the pixelated look of favicons for use at desktop icons. I figured I could take this idea a step further and render 3D images from any given 16x16 image, using a combination of Python, Mako templates and Povray – the same combination of technologies I used for my (now defunct) 3D pie chart project.

3D favicons

Three dimensional favicons!

I knocked up the scripts to pass the time on a long train journey recently. The results were encouraging! The image insert shows a few of the icons I generate with the first pass at this. Some turn out better than others, but they would all work as recognizable icons.

The Code

The code is not too complex, and consists of a single Python script that uses Python Image Library to read an image and scan each pixel. The results of the image scanning process are then passed to a Mako template, which produces a Povray scene file.

#!/usr/bin/env python

import Image
from math import *

class Col(object):

    def __init__(self, r, g, b, a=1.0):
        self.r = r
        self.g = g
        self.b = b
        self.a = a

class ImageScan(object):

    def __init__(self, image):

        self.image = image

    def scan(self):

        w, h = self.image.size
        step_w = 1.0 / w
        step_h = 1.0 / h
        self.pixel_x_size = step_w
        self.pixel_y_size = step_h

        im = self.image
        getpixel = im.getpixel

        pixels = []

        for y in range(h):

            row = []
            for x in range(w):

                c = getpixel( (x, h-1-y) )

                # Skip transparent, and almost transparent pixels
                if c[-1] > 32:
                    c = Col( *(i/255.0 for i in c) )
                    ix = x * step_w
                    iy = y * step_h
                    row.append( (ix, iy, c) )


        self.pixels = pixels

    def __iter__(self):
        return iter(self.pixels)

if __name__ == "__main__":

    import sys
    filename = sys.argv[1]

    image =
    image = image.convert('RGBA')

    ims = ImageScan(image)

    td = {}
    td['image'] = ims

    from mako.template import Template

    output_filename = filename.split('.')[0] + '_favicon.png'

    mytemplate = Template(filename='favicon.pov')
    open('out.pov', 'w').write(mytemplate.render(**td))

    import os
    os.system('povray out.pov -O%s +A0.1 -w128 -h128 +UA +R8' % output_filename)
    print "Output", output_filename

Povray scene files are kind of like a half-way point between data and a programming language; there are variables and expressions and some control structures, but when you combine them with a templating system, like Mako, it creates a remarkably flexible system where 3D images can be programatically generated.

The favicon template loops through the scanned image data passed to it by the script. This data contains the location and colour of each visible pixel, which the template uses to generate a coloured cubiod in the scene, per pixel. These cuboids combine to create a three dimensional version of the original icon image.

#version 3.6;

#declare pixel_finish =
  finish {
    ambient rgb <1.0,1.0,1.0>*.1
    brilliance  2.54
    crand       0.000
    diffuse     0.813
    metallic    1.5000
    phong       2.093
    phong_size  1.000
    specular    0.425
    roughness   0.001

#declare Camera0 =
camera {
  location <2.173,1.520+1,5.820>/2
  up y
  right -1.000*x
  angle 22.000
  sky <-0.083,0.971,-0.223>
  look_at < 0, 0, 0 >

light_source {
  < 2, 4, 10 >, color rgb <1, 1, 1>*.9

light_source {
    <12, 1, -1.5>, color rgb <.3, .3, .6>

light_source {
    <0, 10, -1.5>, color rgb <.6, .3, .3>

union {
% for row in image:
% for x, y, c in row:
box {
    <${x-.5}, ${y-.5}, -0.5> , <${x-.5 + image.pixel_x_size}, ${y-.5 + image.pixel_y_size}, 0.5>
    texture {
        pigment { color rgbft <${c.r}, ${c.g}, ${c.b}, 0, 0.0> }
        finish { pixel_finish }
    scale <1, 1, 0.4>
% endfor
% endfor

camera{ Camera0 }

Because it is such a simple scene, Povray takes just a second to render a 128x128 image. So quick that it may be practical to render these image on-the-fly when required. Or I may just render a few thousand favicons for popular sites and upload them.

Hopefully you will see these 3D favicons on in the coming weeks…

This blog post was posted to It's All Geek to Me on Friday October 9th, 2009 at 4:42PM

6 Responses to "Turning website favicons in to 3D"

  • October 9th, 2009, 5:35 p.m.

    I like it. I don't understand it, but I sure do like it.

  • October 9th, 2009, 7:22 p.m.

    You really should handle alpha correctly. The resulting translucent boxes should look pretty nice. Well, at least they will look better than these incorrect opaque ones.

  • October 9th, 2009, 7:30 p.m.

    Jos, I actually tried mapping the image alpha to transparency, but it didn't look good, because you could see through the translucent ‘pixels’ and see edges and other pixels, which made the resulting image very noisy.

  • triton
    October 10th, 2009, 1:25 a.m.

    It's a nice hack, but, sorry for being honest, looks like crap. :D

  • October 10th, 2009, 1:10 p.m.

    It may take me some time to understand it …..however it looks cool !

  • kl
    October 15th, 2009, 12:51 a.m.

    make semi-transparent pixels fully opaque, but not as deep (i.e. 50% opaque pixel would end at half depth of the icon).

Leave a Comment

You can use bbcode in the comment: e.g. [b]This is bold[/b], [url][/url], [code python]import this[/code]
Preview Posting...
Previewing comment, please wait a moment...

My Tweets

Will McGugan

My name is Will McGugan. I am an unabashed geek, an author, a hacker and a Python expert – amongst other things!

Search for Posts
Possibly related posts
Popular Tags
Recent Comments
def thousands_with_commas(number): new_number = [] number = str(number) mod_value = len(number) % 3 counter = 3 if len(number) 4: return ...
don't know why this was tempting.. (#1)import re from collections import Counter, OrderedDict cnt=Counter() with open(./t) as f: #--- strip ...
- Mike on Python Coder Test
Hello! I've seen this test and tried to do them. Result added bellow. First path: def thousands_with_commas(i): i = str(i) ...
Why another framework? what wrong with django, pyramid, flask?will be have answer for this question in the docs)
Hi! Really great code, good work! But trying to use it on a responsive site, it didn't resize images. So, ...
© 2008 Will McGugan.

A technoblog blog, design by Will McGugan