Created with PulpCore

SceneGraph.java 

// SceneGraph
// Mouse over a stem to make it sway. Click to generate a new tree.
// Shows how Scene2D internally uses a scene graph: Groups can have child Groups, and children
// inherit their parent's transform.
import pulpcore.animation.Color;
import static pulpcore.image.Colors.*;
import pulpcore.Input;
import pulpcore.math.CoreMath;
import pulpcore.scene.Scene2D;
import pulpcore.sprite.FilledSprite;
import pulpcore.sprite.Group;
import pulpcore.sprite.Sprite;
import pulpcore.Stage;

public class SceneGraph extends Scene2D {
    
    int maxDepth = 10;
    int startSize = 150;
    int trunkColor = rgb(0x3b2300);
    int leafColor = rgb(0x468207);
    
    @Override
    public void load() {
        add(new FilledSprite(BLACK));
        
        Group trunk = new Group(Stage.getWidth() / 2, Stage.getHeight());
        generateTree(trunk, 0);
        add(trunk);
    }
    
    @Override
    public void update(int elapsedTime) {
        if (Input.isMousePressed()) {
            getMainLayer().removeAll();
            load();
        }
    }
    
    private void generateTree(Group parent, int depth) {
        if (depth >= maxDepth) {
            return;
        }
        // Blend the color from trunk to stem
        Color color = new Color();
        color.animate(trunkColor, leafColor, maxDepth);
        color.update(depth);
        
        // Create this stem
        double w = (startSize/3) / (Math.pow(depth, 1.5)+1);
        double h = startSize / (Math.pow(depth, 0.9)+1);
        if (depth > 0) {
            h *= CoreMath.rand(0.8, 1.2);
        }
        Stem stem = new Stem(0, 0, w, h, color.get());
        stem.setAnchor(Sprite.SOUTH);
        parent.add(stem);
        
        // Add two child stems (using recursion)
        double x = w/4.0;
        double y = -h + w/6.0;
        
        // Left stem
        Group leftChild = new Group(-x, y);
        leftChild.angle.set(CoreMath.rand(-0.7, -0.3));
        generateTree(leftChild, depth + 1);
        parent.add(leftChild);
        parent.moveToBottom(leftChild);
        
        // Right stem
        Group rightChild = new Group(x, y);
        rightChild.angle.set(CoreMath.rand(0.3, 0.7));
        generateTree(rightChild, depth + 1);
        parent.add(rightChild);
        parent.moveToBottom(rightChild);
    }
    
    public static class Stem extends FilledSprite {
        
        double angleJitter;
        
        public Stem(double x, double y, double w, double h, int color) {
            super(x, y, w, h, color);
        }
        
        @Override
        public void update(int elapsedTime) {
            // Make the stem (and it's children) sway
            Group parent = getParent();
            if (Input.isMouseMoving() && !parent.angle.isAnimating() && isMouseOver()) {
                double maxAngleJitter = 1.5 / width.get();
                double newAngleJitter = CoreMath.rand(-maxAngleJitter, maxAngleJitter);
                double newAngle = parent.angle.get() - angleJitter + newAngleJitter;
                parent.angle.animateTo(newAngle, 100);
                angleJitter = newAngleJitter;
            }
        }
    }
}