Close
Simone Seagle

Simone Seagle

Independent Web and Educational Software Developer

Read More

Search

Play

Chrysanthemums by a Stream

Mouse-over or touch/drag to interact with Chrysanthemums by a Stream Or, view fullscreen.

About the Art

This wonderful folding screen is one of a pair painted by Ogata Kōrin or somewhere around the early 1700s. Ogata Kōrin is regarded as one of the greatest painters of the Edo Period (1603 - 1868) in Japan. This incredible period of art produced many iconic works, including the Great Wave. Kōrin was famous for producing these folding screens, or byōbu. He was born into a wealthy textile merchant family, and only became an established artist later in life. He the title "Bridge of the Dharma," which is one of the highest honors awarded to Buddhist artists. The signature above the stream on the far right contains his name and this title.

I found this piece via The Public Domain Review, and it is part of the Cleveland Museum of Art's Collection. It was a dream to work with because it is so stylized and beautiful. I wanted to keep the animation subtle so it would look like the chrysanthemums were gently waving back and forth in the wind.

About the Programming

Because this piece has such clean, crisp, lines, I was easily able to lift the chrysanthemums away from the screen behind them using Photoshop's color range selection tool. Once they were isolated, I was able to use basically the same technique I used for the Repast of the Lion to get them to sway. This was infinitely easier, though, I have to say.

I started PixiJS's rope mesh object and a texture for each of the chrysanthemums. The rope mesh uses an array of points in a line that map to a texture. By modulating the points, you wind up with an image that looks like it's wobbling. They have an example of this in action on a snake. Once the mesh is done, I update it every frame with either a gentle wave, or a gentle interaction with the cursor.

var pointLength = chrysanthemum.points.length;
    for (var i = 0; i < pointLength; i++) {
        // The flower's bottom should not move, so damp it accordingly
        var damp = i / pointLength;
        var xWobble = 7;

        var index = Math.floor(size.width / chrysanthemum.x);

        if (trackLoc.x > 0) {
            // If interacting, we need to apply an offset to make sure it accounts for the mouse being in the right spot
            var xOffset = chrysanthemum.points[i].x + chrysanthemum.x;
            var yOffset = chrysanthemum.points[i].y + chrysanthemum.y;

            var pointDis = distance(trackLoc.x, trackLoc.y,
                         xOffset, yOffset);

            if (pointDis < maxDist) {
                xWobble += 8 * (maxDist - pointDis)/maxDist;
            }
        }

        // Make the plant sway
        chrysanthemum.points[i].x = (Math.sin(((i + index + frame) * 0.02)) * xWobble) * damp;
    }

The water is made using a tiling sprite with a mask. To make it look like it's flowing, I change the offset of the tiles and their scale. I also wanted it to not be even, so I update both the x and the y with a sine function so it bobs a little bit.

function updateWater() {
    // I tween the differences in velocity and scale so that it doesn't look jerky
    var deltaV = {
        x: waterBaseSpeed - waterVelocity.x,
        y: waterBaseSpeed - waterVelocity.y
    };
    var ds = 1 - scaleMod;

    if (trackLoc.x > 0 && Math.abs(trackLoc.vx) > 0) {
        //console.log(trackLoc);
        // tween it to the velocity we want - the velocity plus the tracking one
        deltaV = {
            x: waterBaseSpeed*( -0.08 * trackLoc.vx - 1),
            y: waterBaseSpeed*( -0.08 * trackLoc.vy - 1)
        };

        // Take the average of the tracking location as fractions
        var ds = (trackLoc.x/size.width + trackLoc.y/size.height) - scaleMod;
    } 

    // Only add the delta fraction if it's big enough to matter.
    if (Math.abs(deltaV.x) > 0.001)
        waterVelocity.x += deltaV.x/20.0;
    if (Math.abs(deltaV.y) > 0.001)
        waterVelocity.y += deltaV.y/20.0;
    if (Math.abs(ds) > 0.001)
        scaleMod += ds/20.0;

    var updatedXV = waterVelocity.x + (waterVelocity.x * 0.1)*Math.sin(frame/83);
    var updatedScale = (1 + 0.05 * scaleMod * Math.cos(frame/97)) * scale;
    var deltaScaleMod = updatedScale - waterScale;

    // In order to counteract the optical illusion of the water flowing backwards when the tile is scaled up, I subtract the scale difference times a constant that looked pretty good.
    if (deltaScaleMod > 0) 
        updatedXV -= 2000.0*deltaScaleMod;

    waterScale = updatedScale;

    // Now update the x and y tiles
    water.tilePosition.x += updatedXV;
    water.tilePosition.y += waterVelocity.y * -1.0 * Math.cos(frame/53);
    water.tileScale.x = water.tileScale.y = updatedScale;
}

Sometimes there are a lot of strange looking constants in these kinds of projects. I wish there was some brilliant mathematical reason why I chose them, but basically I just tweak them until it looks the way I want. They're the little knobs I can adjust until the behavior is as convincing as I imagined it to be.