splitbrain.org

electronic brain surgery since 2001

Nyan Cat on DokuWiki

On April 1st, I always try to add a little joke to the DokuWiki website. In the past we had upside-down headlines, a flying duck you could shoot and pink unicorns. This year we had Nyan cat:

Some people asked how this was done, so here's how (including the code).


The idea is pretty simple: when you move the mouse, the cursor changes to Nyan cat that leaves a trail of rainbow and the (in)famous Nyan cat sound plays. All this is done with a bit of JavaScript using CSS and HTML 5. As this is a bit of bleeding edge technology, I didn't care for IE support at all. It works fine in Firefox and Chrome though.

The Main Loop

All the magic happens only when the mouse is moved. To do this, I registered a mousemove event handler. The event gives access to the mouse position which I compare with the last position. When some movement was detected the magic starts:

onMouseMove: function(evt){                                                                                         
    var mousePos = nyan.getMousePos(nyan.wrapper, evt);                                                             
 
    if(!nyan.last){                                                                                                 
        nyan.last = mousePos;                                                                                       
        return;                                                                                                     
    }                                                                                                               
 
    if(Math.abs(nyan.last.x - mousePos.x) >= 1 ||                                                                   
        Math.abs(nyan.last.y - mousePos.y) > 1) { 
        // magic here
    }
}

The Rainbow

My goal was to have the site continue to work normally. That means the text needs to remain readable (and selectable), links can be clicked, images be viewed. To achieve that, the rainbow has to be drawn behind the content.

For drawing the HTML5 canvas element is used, so that element needs to be positioned behind the real content. Because I didn't want to modify any of the site's HTML, it had to be done in JavaScript. My solution was to move all of the content into a wrapper div1) and then apply a z-index:

nyan.wrapper = document.createElement('div');                                                                   
while (document.body.firstChild) {                                                                              
    nyan.wrapper.appendChild(document.body.firstChild);                                                         
}                                                                                                               
nyan.wrapper.style.zIndex = 5;                                                                                  
document.body.appendChild(nyan.wrapper);

Since a div is usually styled differently than the body, this may create a site that looks slightly different. I experimented a bit with copying the body styles to the wrapper diff. This works fine in my demo but actually made it worse on dokuwiki.org where I removed it again. There are probably better solutions.

The Cat

The cat itself is relatively simple. The CSS cursor attribute accepts a url() parameter to reference a local image. Unfortunately not all browsers support an animated GIF here. So instead I used 12 different images and loop through them in the main loop. And because I don't wanted it to fly backwards, I have a left and right facing version of each animation step.

The Sound

For playing the sound I make use of HTML5's audio tag. Because different browsers support different audio formats I added the sound as MP3 and OGG file and set the source accordingly.

Every time a mouse movement is detected, I start playing the music from its current position and register a timer to stop again in a half second. Because the timer is overwritten as long as the mouse moves, the music continues to play. When you stop the mouse, the music stops with a half second. This makes sure the music doesn't stutter.

One problem I found is that looping sounds with the loop attribute does not reliably work in Chrome. To cope with that I simply registered a ended event handler on the audio tag that sets the play marker back to the start:

nyan.sound.addEventListener('ended',function(){                                                                 
    this.currentTime = 0;                                                                                       
}, false);

Code and Demo

1)
StackOverflow had some info on how to do this