Index: /trunk/src/org/openstreetmap/josm/gui/GettingStarted.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/GettingStarted.java	(revision 19253)
+++ /trunk/src/org/openstreetmap/josm/gui/GettingStarted.java	(revision 19254)
@@ -7,4 +7,8 @@
 import java.awt.EventQueue;
 import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.event.MouseMotionListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
@@ -15,8 +19,10 @@
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
 import javax.swing.Timer;
 import javax.swing.border.EmptyBorder;
 import javax.swing.event.HyperlinkEvent;
 import javax.swing.event.HyperlinkListener;
+import javax.swing.event.MouseInputListener;
 
 import org.openstreetmap.josm.actions.DownloadAction;
@@ -24,4 +30,5 @@
 import org.openstreetmap.josm.actions.HistoryInfoAction;
 import org.openstreetmap.josm.data.Version;
+import org.openstreetmap.josm.gui.animation.AnimationExtension;
 import org.openstreetmap.josm.gui.animation.AnimationExtensionManager;
 import org.openstreetmap.josm.gui.datatransfer.OpenTransferHandler;
@@ -48,5 +55,5 @@
  * contains news about recent major changes, warning in case of outdated versions, etc.
  */
-public final class GettingStarted extends JPanel implements ProxyPreferenceListener {
+public final class GettingStarted extends JPanel implements ProxyPreferenceListener, MouseInputListener {
 
     private final LinkGeneral lg;
@@ -155,4 +162,9 @@
         add(scroller, BorderLayout.CENTER);
 
+        scroller.addMouseMotionListener(this);
+        scroller.addMouseListener(this);
+        lg.addMouseMotionListener(this);
+        lg.addMouseListener(this);
+
         getMOTD();
 
@@ -177,8 +189,54 @@
 
     @Override
+    public void mouseMoved(MouseEvent e) {
+        final AnimationExtension extension = AnimationExtensionManager.getExtension();
+        if (extension instanceof MouseMotionListener) {
+            ((MouseMotionListener) extension).mouseMoved(e);
+        }
+    }
+
+    @Override
+    public void mouseDragged(MouseEvent e) {
+        // Ignored
+    }
+
+    @Override
+    public void mousePressed(MouseEvent e) {
+        final AnimationExtension extension = AnimationExtensionManager.getExtension();
+        if (extension instanceof MouseListener) {
+            ((MouseListener) extension).mousePressed(e);
+        }
+    }
+
+    @Override
+    public void mouseEntered(MouseEvent e) {
+        // Ignored
+    }
+
+    @Override
+    public void mouseExited(MouseEvent e) {
+        // Ignored
+    }
+
+    @Override
+    public void mouseReleased(MouseEvent e) {
+        final AnimationExtension extension = AnimationExtensionManager.getExtension();
+        if (extension instanceof MouseListener) {
+            ((MouseListener) extension).mouseReleased(e);
+        }
+    }
+
+    @Override
+    public void mouseClicked(MouseEvent e) {
+        // Ignored
+    }
+
+    @Override
     public void paint(Graphics g) {
         super.paint(g);
         if (isShowing()) {
-            AnimationExtensionManager.getExtension().adjustForSize(getWidth(), getHeight());
+            Point p = new Point(0, 0);
+            SwingUtilities.convertPointToScreen(p, this);
+            AnimationExtensionManager.getExtension().adjustForSize(getWidth(), getHeight(), p.x, p.y);
             AnimationExtensionManager.getExtension().animate();
             AnimationExtensionManager.getExtension().paint(g);
Index: /trunk/src/org/openstreetmap/josm/gui/animation/AnimationExtension.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/animation/AnimationExtension.java	(revision 19253)
+++ /trunk/src/org/openstreetmap/josm/gui/animation/AnimationExtension.java	(revision 19254)
@@ -16,6 +16,8 @@
      * @param w width
      * @param h height
+     * @param x x origin of view area
+     * @param y y origin of view area
      */
-    void adjustForSize(int w, int h);
+    void adjustForSize(int w, int h, int x, int y);
 
     /**
Index: /trunk/src/org/openstreetmap/josm/gui/animation/AnimationExtensionManager.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/animation/AnimationExtensionManager.java	(revision 19253)
+++ /trunk/src/org/openstreetmap/josm/gui/animation/AnimationExtensionManager.java	(revision 19254)
@@ -3,4 +3,5 @@
 
 import java.time.LocalDate;
+import java.time.Month;
 import java.time.ZoneId;
 
@@ -28,6 +29,12 @@
     public static AnimationExtension getExtension() {
         if (currentExtension == null) {
-            currentExtension = Boolean.TRUE.equals(PROP_ANIMATION.get()) && isChristmas() ? new ChristmasExtension()
-                    : new NoExtension();
+            final boolean isAnimated = Boolean.TRUE.equals(PROP_ANIMATION.get());
+            if (isAnimated && isChristmas()) {
+                currentExtension = new ChristmasExtension();
+            } else if (isAnimated && isBirthday()) {
+                currentExtension = new BirthdayExtension();
+            } else {
+                currentExtension = new NoExtension();
+            }
         }
         return currentExtension;
@@ -46,3 +53,12 @@
         return LocalDate.now(ZoneId.systemDefault()).getDayOfYear() > 350;
     }
+
+    /**
+     * The first commit of JOSM to svn (r1) was on 2005-09-27
+     * @return {@code true} if today is JOSM's birthday
+     */
+    private static boolean isBirthday() {
+        LocalDate l = LocalDate.now(ZoneId.systemDefault());
+        return l.getMonth() == Month.SEPTEMBER && l.getDayOfMonth() == 27;
+    }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/animation/BirthdayExtension.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/animation/BirthdayExtension.java	(revision 19254)
+++ /trunk/src/org/openstreetmap/josm/gui/animation/BirthdayExtension.java	(revision 19254)
@@ -0,0 +1,344 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.animation;
+
+import java.awt.event.MouseMotionListener;
+import java.util.ArrayList;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Dimension;
+import java.awt.Image;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+
+import javax.swing.JPanel;
+
+import org.openstreetmap.josm.tools.ImageProvider;
+
+/**
+ * Birthday animation game extension.
+ * @author Pauline Thiele
+ * @since xxx
+ */
+public class BirthdayExtension extends JPanel implements AnimationExtension, MouseListener, MouseMotionListener {
+    private static final int SIZE = 40;
+    private final Image myImage;
+    private double w;
+    private double h;
+    private double Mx;
+    private double My;
+    private int originX;
+    private int originY;
+    private boolean isClicked;
+    private boolean isHappyBirthday;
+    private int giftNumber = 1;
+
+    private final ArrayList<Gift> giftList = new ArrayList<>();
+    private long startTime;
+    private long lastSpawnTime;
+
+    /**
+     * Creates a circle with a position, radius and checks if two circles ovelaps
+     */
+    private static final class Circle {
+        double x;
+        double y;
+        double radius = 1;
+
+        void setPosition(double x, double y) {
+            this.x = x;
+            this.y = y;
+        }
+
+        void setRadius(double radius) {
+            this.radius = radius;
+        }
+
+        boolean overlaps(Circle other) {
+            double dx = x - other.x;
+            double dy = y - other.y;
+            double dr = radius + other.radius;
+            return dx*dx + dy*dy < dr*dr;
+        }
+    }
+
+    /**
+     * Creates a player with a position, radius and updates position
+     */
+    private final class Player {
+        Circle circle = new Circle();
+
+        void setPosition(double x, double y) {
+            circle.setPosition(x, y);
+        }
+
+        void setRadius(double radius) {
+            circle.setRadius(radius);
+        }
+
+        void update() {
+            setPosition(Mx, My);
+        }
+    }
+
+    /**
+     * Creates a gift with a position, radius, updates position and renders the image
+     */
+    private final class Gift {
+        Image myImage = getImage();
+        Circle circle = new Circle();
+        int offsX = 4;
+        int offsY = 4;
+
+        void setPosition(double x, double y) {
+            circle.setPosition(x, y);
+        }
+
+        void setRadius(double radius) {
+            circle.setRadius(radius);
+        }
+
+        void update() {
+            circle.x += offsX;
+            circle.y += offsY;
+
+            if (circle.x <= 0) { // left
+                offsX *= -1;
+                circle.x = 0;
+                if (circle.y == 0) {
+                    offsY *= -1;
+                }
+            } else if (circle.x >= (w - SIZE)) { // right
+                offsX *= -1;
+                circle.x = w - SIZE;
+            }
+
+            if (circle.y <= 0) { // top
+                offsY *= -1;
+                circle.y = 0;
+            } else if (circle.y >= (h - SIZE)) { // bottom
+                offsY *= -1;
+                circle.y = h - SIZE;
+            }
+        }
+
+        void render(Graphics g) {
+            double x = circle.x;
+            double y = circle.y;
+            g.drawImage(myImage, (int) x, (int) y, null);
+        }
+    }
+
+    Player player = new Player();
+
+    BirthdayExtension() {
+        this.myImage = new ImageProvider("presets/shop/present").
+                setMaxSize(new Dimension(SIZE, SIZE)).get().getImage();
+        player.setRadius(11);
+        lastSpawnTime = System.currentTimeMillis();
+    }
+
+    /**
+     * Creates gifts on a random position on screen
+     */
+    void spawnGifts() {
+        final byte maxGifts = 30;
+        if (giftList.size() < maxGifts) {
+            lastSpawnTime = System.currentTimeMillis();
+
+            Gift gift = new Gift();
+            giftList.add(gift);
+            gift.myImage = getImage();
+            gift.setRadius(32);
+
+            double x = (w - originX) / 2;
+            double y = (h - originY) / 2;
+
+            if (giftList.size() > 1) {
+                double radius = w / 2;
+                double angle = 2 * Math.PI * Math.random();
+                x += Math.cos(angle) * radius;
+                y += Math.sin(angle) * radius;
+                gift.setPosition(x, y);
+            } else {
+                gift.setPosition(x, y);
+            }
+        }
+    }
+
+    /**
+     * Manages the creation, updating and deletion of gifts
+     */
+    private void gameLogic() {
+        if ((giftList.isEmpty() && !isHappyBirthday) ||
+            (System.currentTimeMillis() - lastSpawnTime > 5000 && !isHappyBirthday)) {
+            spawnGifts();
+            lastSpawnTime = System.currentTimeMillis();
+        }
+
+        player.update();
+
+        for (Gift gift : giftList) {
+            gift.update();
+        }
+
+        int index = 0;
+        for (Gift gift : giftList) {
+            if (gift.circle.overlaps(player.circle) && isClicked) {
+                startTime = System.currentTimeMillis();
+                giftList.remove(index);
+                break;
+            }
+            index += 1;
+        }
+    }
+
+    @Override
+    public void mousePressed(MouseEvent e) {
+        isClicked = true;
+    }
+
+    @Override
+    public void mouseReleased(MouseEvent e) {
+        isClicked = false;
+    }
+
+    @Override
+    public void mouseEntered(MouseEvent e) {}
+
+    @Override
+    public void mouseExited(MouseEvent e) {}
+
+    @Override
+    public void mouseClicked(MouseEvent e) {}
+
+    @Override
+    public void mouseDragged(MouseEvent e) {}
+
+    @Override
+    public void mouseMoved(MouseEvent e) {
+        Mx = e.getXOnScreen()- originX;
+        My = e.getYOnScreen()- originY;
+
+        player.setPosition(Mx, My);
+
+        huntGift();
+    }
+
+    @Override
+    public final void adjustForSize(int w, int h, int x, int y) {
+        this.w = w;
+        this.h = h;
+        originX = x;
+        originY = y;
+    }
+
+    /**
+     * Visualizes the gifts and the "Happy Birthday JOSM!" text
+     */
+    @Override
+    public void paint(Graphics g) {
+        gameLogic();
+        for (Gift gift : giftList) {
+            gift.render(g);
+        }
+
+        Graphics2D g2d = (Graphics2D) g;
+
+        /* keep a bit of debugging code
+        if (Logging.isTraceEnabled()) {
+            // draws circles around mouse and gifts
+            for (Gift gift : giftList) {
+                Ellipse2D.Double circleG = new Ellipse2D.Double(gift.circle.x - gift.circle.radius / 2,
+                        gift.circle.y - gift.circle.radius / 2, gift.circle.radius * 2, gift.circle.radius * 2);
+                g2d.draw(circleG);
+            }
+            Ellipse2D.Double circleM = new Ellipse2D.Double(player.circle.x - player.circle.radius / 2,
+                    player.circle.y - player.circle.radius / 2, player.circle.radius * 2, player.circle.radius * 2);
+            g2d.draw(circleM);
+        }
+        */
+
+        if (giftList.isEmpty()) {
+            if (System.currentTimeMillis() - startTime <= 5000) {
+                int fontSize = SIZE /2;
+                g2d.setFont(new Font("Arial", Font.BOLD, fontSize));
+                g2d.setColor(Color.RED);
+
+                String text = "Happy Birthday JOSM! ";
+
+                FontMetrics fm = g2d.getFontMetrics();
+                int textWidth = fm.stringWidth(text);
+                int x = 0;
+                int y = SIZE /2;
+
+                for (int i = 0; i < (w / textWidth) + 1; i++) {
+                    g2d.drawString(text, x, y);
+                    g2d.drawString(text, x, (int) h-y);
+                    x += textWidth;
+                }
+                isHappyBirthday = true;
+            } else {
+                giftNumber += 1;
+                for (int i = 0; i < giftNumber; i += 1) {
+                    spawnGifts();
+                }
+                startTime = System.currentTimeMillis();
+                isHappyBirthday = false;
+            }
+        }
+    }
+
+    @Override
+    public void animate() {
+    }
+
+    /**
+     * Hunts the gift
+     */
+    private void huntGift() {
+        for (Gift gift : giftList) {
+            double dx = gift.circle.x - player.circle.x;
+            double dy = gift.circle.y - player.circle.y;
+
+            double len = Math.sqrt(dx*dx + dy*dy);
+
+            if ((dx < -100 || dx > 100) && (dy < -100 || dy > 100)) {
+                dx = 0;
+                dy = 0;
+            } else if (len > 0 && dx != 0 && dy != 0) {
+                dx /= len;
+                dy /= len;
+            }
+
+            double giftSpeed;
+
+            if (dx != 0 && dy != 0 && gift.circle.x == 0 && gift.circle.y == 0) { // top left corner
+                giftSpeed = 5.0f;
+                dy += 5;
+            } else if (dx != 0 && dy != 0 && gift.circle.x >= w - SIZE && gift.circle.y == 0) { // top right corner
+                giftSpeed = 5.0f;
+                dy += 5;
+            } else if (dx != 0 && dy != 0 && gift.circle.x == 0 && gift.circle.y >= h - SIZE) { // bottom left corner
+                giftSpeed = 5.0f;
+                dx += 5;
+            } else if (dx != 0 && dy != 0 && gift.circle.x >= w - SIZE && gift.circle.y >= h - SIZE) { // bottom right corner
+                giftSpeed = 5.0f;
+                dx -= 5;
+            } else {
+                giftSpeed = 3.0f;
+            }
+
+            gift.circle.x += dx * giftSpeed;
+            gift.circle.y += dy * giftSpeed;
+        }
+    }
+
+    private Image getImage() {
+        return this.myImage;
+    }
+}
Index: /trunk/src/org/openstreetmap/josm/gui/animation/ChristmasExtension.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/animation/ChristmasExtension.java	(revision 19253)
+++ /trunk/src/org/openstreetmap/josm/gui/animation/ChristmasExtension.java	(revision 19254)
@@ -29,5 +29,5 @@
 
     @Override
-    public final void adjustForSize(int w, int h) {
+    public final void adjustForSize(int w, int h, int x, int y) {
         int count = w / (2 * (Star.averageStarWidth + 1));
         while (objs.size() > count) {
Index: /trunk/src/org/openstreetmap/josm/gui/animation/NoExtension.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/animation/NoExtension.java	(revision 19253)
+++ /trunk/src/org/openstreetmap/josm/gui/animation/NoExtension.java	(revision 19254)
@@ -16,5 +16,5 @@
 
     @Override
-    public void adjustForSize(int w, int h) {
+    public void adjustForSize(int w, int h, int x, int y) {
         // No-op
     }
