Simone Seagle

Simone Seagle

Independent Web and Educational Software Developer

Read More



Frohe Weihnachten! (Merry Christmas)

Mouse-over or touch/drag to interact with Frohe Weihnachten! Or, view fullscreen.

About the Art

It's been quite a long time since I've had the time to work on these fun open-access pieces. What better time to bring it back than Christmas? My original intent was to animate a Christmas tree and make it appear to glow, but I just couldn't find a picture that didn't look schmaltzy. I wound up finding this piece by Moriz Jung. It's part of a series, all of which are lovely, but unfortunately all of which (other than this one) are still covered by copyright.

Moriz Jung was a Czechoslovakian-born graphic designer who later moved to Austria to work in the Wiener Werkst├Ątte, or Vienna Workshop. He created many wonderfully playful and fun post-cards, many of which can be seen at the Met. For more of his work, there's a great collection here. Sadly, he died in a WWI battle when he was only 30 years old.

About the Programming

There are a couple of things going on in this animation. First, lets talk about the candles and the trees. There's a base luminosity value that wanders around every few frames using a sort of 1D Brownian motion between 0.2 and 1. This is also influenced by the distance to the tracking point - closer is brighter. The brightness of the candles flickers around that value, which is then represented as the scale and opacity of each candle flame.

    function updateCandle(candle) {
        var candleLum = luminosity + (Math.random() * 4 * luminosityDelta - luminosityDelta * 2);

            // Make sure the luminosity of the candle stays between 0 and 1
            candleLum = Math.max(0, Math.min(1, candleLum));

        // This is the sum of all the candles' brightnesses.  We'll use that in a bit.
        candleSum += candleLum;

        candle.targetAlpha = candleLum;
        candle.targetScale = 0.4 + candleLum;

        // Now we tween it so it doesn't look quite so jerky.
        var delta = candle.targetAlpha - candle.alpha;
        var deltaScale = candle.targetScale - candle.scale.x;

        if (Math.abs(delta) > 0.1) {
            candle.alpha = candle.alpha + delta/4;
        if (Math.abs(deltaScale) > 0.1 ) {
            candle.scale.set(candle.scale.x + deltaScale / 4);


After we update the candles, then we use the sum of the candle luminosities to determine which background tree image to use. I started with the original art and Photoshopped it so that there were 5 different versions of the trees with varying amounts of black showing. The brighter the candles, the less black you would see in order to make it look like the light from the candles is shining on the trees behind the monk and the Christmas tree. I use a linear interpolation function to determine the index of which image to use.

    function updateTreeSprites() {
        // The M and B values are calculated elsewhere based on the 5 images and the range of 0-7 for the total brightness of the candles added up.  *y=mx+b* in case you've forgotten your high school algebra. ;-)
        var index = Math.floor(candleLerpM * candleSum + candleLerpB);

        for (var i = 0; i < trees.length-1; i++) {
            if (i == index) {
                trees[i].targetAlpha = 1;
            } else {
                trees[i].targetAlpha = 0;

            var alphaDiff = trees[i].targetAlpha - trees[i].alpha;
            if (Math.abs(alphaDiff) > 0.01) {
                trees[i].alpha = trees[i].alpha + alphaDiff / 20;

Finally, I felt like it needed a little something else, so I added a neat little animation of the monk flipping a page in his book that's triggered only when the luminosity value is over 0.9-ish. That also uses linear interpolation and some rotation and scaling and whatnot. If you want to know how that was done, tweet me or something. For now though, I want to get back to my Christmas Eve.