Dynamic favicons

Google Calendar released a new favicon which prompted a few mentions from friends on Twitter. The first, probably more important was from Cennydd:

New Google Calendar icon most un-Googley. I like it.

Then Danny pitched in with:

I see Google Calendar has a new icon. They could use the current date rather than “31″ though

So let’s fix that shall we?

How it’s done

The trick here is to use a canvas element and export the image data as a png. Since favicons can be pngs then we know this can work. Note that this technique has been done before.

The setup is needing to create two calendar images: one with the date if this doesn’t work (defaulting to something like 31) and the second without the date text. For whatever reason, I went and replicated the Google Calendar icon in Photoshop:

favicon without text favicon with text

The plan now is to use the favicon template and lay the text over. Simple.

The minimum starting point

Start your document by including the favicon link element in the head:

<link id="favicon" rel="icon" type="image/png" href="ical-icon-complete.png" />

The “complete” version is the favicon with the 31 on it already. Next we’ll use JavaScript to dynamically create the favicon.

Using a canvas for dynamic favicons

We need the following items to make this effect work:

  1. A canvas that doesn’t have to live in the DOM, that’s 16×16 – our favicon size
  2. The template favicon image
  3. Once, and only once, the template image has loaded, we then go adding the text
  4. The date in a two character format, i.e. 04 is the 4th

That’s it. For connivence I’ve added an id to the link element so that I can just change the href when the image is ready. The following JavaScript can be included anywhere below the link element:

(function () {
var canvas = document.createElement('canvas'),
    ctx,
    img = document.createElement('img'),
    link = document.getElementById('favicon').cloneNode(true),
    day = (new Date).getDate() + '';

if (canvas.getContext) {
  canvas.height = canvas.width = 16; // set the size
  ctx = canvas.getContext('2d');
  img.onload = function () { // once the image has loaded
    ctx.drawImage(this, 0, 0);
    ctx.font = 'bold 10px "helvetica", sans-serif';
    ctx.fillStyle = '#F0EEDD';
    if (day.length == 1) day = '0' + day;
    ctx.fillText(day, 2, 12);
    link.href = canvas.toDataURL('image/png');
    document.body.appendChild(link);
  };
  img.src = 'ical-icon.png';
}

})();

The important part is the order in which the code runs. It creates an image element, and hooks an onload event handler. When this onload event runs, it draws the image on to the canvas using ctx.drawImage(this, 0, 0). this refers to the image the onload event acted upon, and 0, 0 is the top, left position to start drawing.

Next we style the text and draw it on.

Finally using canvas.toDataURL we set a new href to the link.

You should be able to see the code running on this blog post, if you look at the favicon associated with this page, it should be the calendar icon with the correct date if your browser supports the canvas API (IE8 and below don’t, all other browsers do).

Try changing the date to test it out.

A few people having pointed out that in fact trying to change favicons in IE is not possible. I’ve read around this a bit now, and it looks like there isn’t a way – good thing we’re just adding a sprinkle of sexiness then, eh?

23 Responses to “Dynamic favicons”

  1. Awesome! I’ve always thought of doing this… i even suggested the email team to use this to display the Unread count (if small enough to fit into 16px.. if not, display the infinity symbol instead. :P)

    Thanks for giving me an article to point them to. \o/

    Well done.

  2. Awesome!

  3. Cool :)

    Doesn’t seem to work in FF 3.6.8 on either OSX or Windows platforms though…

  4. I’d like to see Google implement this for both Calendar and Gmail…

  5. Using Firefox 3.6.8 on Windows – it’s still your default remy-favicon.png.

  6. What? Cennydd’s was more important? I’m massively offended.

  7. Nice, I’ve noticed some browsers cache the favicon *very* hard, does it have any effect on this?

  8. FF 4.0b gets the new ‘dated’ favicon! Amazing, really cool :)

  9. Works fine on Mac OS X SnowLeaopard / Safari 3 :)

  10. It’s working for me on Safari 5.0 (6533.16) on Snow Leopard.

  11. Is it possible to turn this into a greasemonkey script or something?

  12. Think my comments are getting spam-screened for some reason. Trying one more time:

    Just thought you might like to know I got this working on the actual Google Calendar site using Greasemonkey. Userscript here: http://userscripts.org/scripts/show/84382

  13. @Ramenkage – I manually moderate my comments because spam gets through sometimes – hence why you weren’t appearing straight away.

  14. Oh, interesting! So if you clone and append the link (favicon) element to the -body-, that works in Safari, eh? Matthieu “P01″ Henri (Defender Of The Favicon) and I were trying to remove and replace the link element within the head element for animation purposes, expecting that to be proper behaviour, and this technique only worked in Firefox and Opera – seemed like Safari wouldn’t recognize changes in the head after the page had loaded.

    (Edit: Or, re-reading, maybe it only changes once and further attempts don’t work? Hrm.)

  15. @Scott – hmm – that’s odd – I thought it wasn’t working in Safari, but now it’s definitely appearing to work. However, on testing it now, it looks like I can’t modify it again when I append a second or third favicon – it just sticks to what was on the page.

  16. Great post! I love little tips like this that just enhance the user’s experience that much more.

    Another way to do this that would be cross-browser compatible would be to have your favicon url point to a handler of some sort (whether you’re using PHP, Java, ASP.Net, etc). This handler, when called, could grab the localization info from the request and create the proper icon and send it back. Since this is all done on the server the browser doesn’t even know it’s a dynamic icon.

    Better yet, your handler could save the file once it’s created. When called, it can first see if an image already exists and send that, otherwise create and save a new one. This way over time you will have a full set of dynamically created icons.

    If your server can add the date as part of the favicon url, such as:

    then if the browser caches it that’s OK. Assuming the browser is caching the favicon url (and not the domain url itself) it will reload the new day’s favicon when the date changes.

  17. What if you used browser detection, and if it’s a flavor of IE then write the numbers using VML (supports VML from version 5.5)? Would something like that be possible?

  18. Last time I checked Defender of the Favicon worked fine in Safari & Chrome. ( Just checked in the Chrome dev channel ).Notice that I do replace the LINK element in the HEAD of the document.

  19. RE @J — If you detected which browsers didn’t support this, I would think you could simply inject a server-side script to do the generation using the language of your choice (.NET in my case)… you could use the src of the fav icon as : getDayIcon.aspx or something similar…and use GDI and write the img to the response stream.

  20. Works fine on latest dev Chrome on Snow Leo.

    P.S. It’s cool but I prefer to do it on the server side.

  21. Hey Remy,

    Really loving the dynamic icon..
    I know this is probably not possible, but, I thought I might as well mention it.
    I have Google Calendar bookmarked. It does update every time I click it, but its not truly dynamic.
    New project?

    Regards,
    Robert (chrome user)

  22. Thanks, I want use this script at Google Toolbar for Safari. http://goo.gl/SEOqK

  23. That’s actually pretty good thinking there, thanks for sharing the code, will try to use this technique on one of my sites.
    Cheers

Leave a Reply
Not required

CODE: Please escape code and wrap in <pre><code>, doing so will automatically syntax highlight