diff --git a/templates/_folder_Lectures/Lecture 15 - handout/main.cpp b/templates/_folder_Lectures/Lecture 15 - handout/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..adfbb4e37a18ceb251fc509c0a87a4ada6c18e62
--- /dev/null
+++ b/templates/_folder_Lectures/Lecture 15 - handout/main.cpp	
@@ -0,0 +1,132 @@
+#include <vector>
+#include <random>
+#include "AnimationWindow.h"
+
+struct Point2D {
+    double x = 0;
+    double y = 0;
+};
+
+struct Direction2D {
+    double x = 0;
+    double y = 0;
+};
+
+constexpr double gravity = 0.1;
+constexpr double explosionPower = 2.0;
+
+struct Rocket {
+    std::vector<Point2D> particleLocations;
+    std::vector<Direction2D> particleDirections; 
+    bool hasExploded = false;
+    bool hasEnded = false;
+    int fadeTimeRemaining = 1000;
+    TDT4102::Color colour;
+
+    Rocket() {}
+    Rocket(int particleCount, int fadeTime, Point2D start, Direction2D launchDirection, TDT4102::Color colour) : 
+        fadeTimeRemaining(fadeTime),
+        colour(colour),
+        particleLocations(particleCount, start), 
+        particleDirections(particleCount, launchDirection) {}
+
+    void update() {
+        if(hasExploded && fadeTimeRemaining == 0) {
+            hasEnded = true;
+        } else if(hasExploded) {
+            fadeTimeRemaining--;
+        } else if(particleDirections.at(0).y > 0 && !hasExploded) {
+            hasExploded = true;
+            std::random_device device;
+            std::default_random_engine engine(device());
+            std::uniform_real_distribution directionDist(0.0, M_PI * 2.0);
+            std::uniform_real_distribution powerDist(0.0, explosionPower * explosionPower);
+
+            for(unsigned int i = 0; i < particleLocations.size(); i++) {
+                // Choose a random direction and velocity for each particle in the rocket
+                double angle = directionDist(engine);
+                double power = powerDist(engine);
+                // This results in a more even spread of particles
+                power = sqrt(power);
+
+                // Using sine and cosine gives us a vector of length 1,
+                // which we multiply by the power
+                particleDirections.at(i).x = cos(angle) * power;
+                constexpr int bonusVerticalForce = 4;
+                particleDirections.at(i).y = sin(angle) * power - bonusVerticalForce;
+            }
+        }
+
+        if(!hasEnded) {
+            for(unsigned int i = 0; i < particleDirections.size(); i++) {
+                particleDirections.at(i).y += gravity;
+            }
+            for(unsigned int i = 0; i < particleLocations.size(); i++) {
+                particleLocations.at(i).x += particleDirections.at(i).x;
+                particleLocations.at(i).y += particleDirections.at(i).y;
+            }
+            if(hasExploded) {
+                if(colour.redChannel > 1) { colour.redChannel-=2; }
+                if(colour.greenChannel > 1) { colour.greenChannel-=2; }
+                if(colour.blueChannel > 1) { colour.blueChannel-=2; }
+                if(colour.redChannel < 2 && colour.greenChannel < 2 && colour.blueChannel < 2) { 
+                    hasEnded = true; 
+                }
+            }
+        }
+    }
+
+    bool isComplete() {
+        return hasEnded;
+    } 
+
+    void draw(TDT4102::AnimationWindow &window) {
+        for(unsigned int i = 0; i < particleLocations.size(); i++) {
+            Point2D location = particleLocations.at(i);
+            constexpr int particleWidthPixels = 2;
+            constexpr int particleHeightPixels = 2;
+            window.draw_rectangle({int(location.x), int(location.y)}, particleWidthPixels, particleHeightPixels, colour);
+        }
+    }
+};
+
+int main() {
+    constexpr int rocketCount = 300;
+    constexpr int particleCount = 500;
+    constexpr int defaultFadeTime = 250;
+    constexpr double minVerticalSpeed = 7.0;
+    constexpr double maxVerticalSpeed = 12.0;
+
+    TDT4102::AnimationWindow window;
+    std::vector<Rocket> rockets;
+    rockets.resize(rocketCount);
+
+    std::random_device device;
+    std::default_random_engine engine(device());
+    std::uniform_int_distribution xDist(0, window.width());
+    std::uniform_real_distribution speedDist(-minVerticalSpeed, -maxVerticalSpeed);
+    std::uniform_int_distribution colourChannelDist(0, 255);
+
+    for(unsigned int i = 0; i < rockets.size(); i++) {
+        TDT4102::Color randomColour(colourChannelDist(engine), colourChannelDist(engine), colourChannelDist(engine));
+        rockets.at(i) = Rocket(particleCount, defaultFadeTime, {(double)xDist(engine), (double) window.height()}, {0.0, speedDist(engine)}, randomColour);
+    }
+    while(!window.should_close()) {
+        window.draw_rectangle({0, 0}, window.width(), window.height(), TDT4102::Color::black);
+        for(unsigned int i = 0; i < rockets.size(); i++) {
+            if(!rockets.at(i).isComplete()) {
+                rockets.at(i).update();
+                rockets.at(i).draw(window);
+            } else {
+                TDT4102::Color randomColour(colourChannelDist(engine), colourChannelDist(engine), colourChannelDist(engine));
+                std::uniform_int_distribution xDist(0, window.width());
+                rockets.at(i) = Rocket(particleCount, defaultFadeTime, {(double)xDist(engine), (double) window.height()}, {0.0, speedDist(engine)}, randomColour);
+            }
+        } 
+        constexpr int estimatedTextWidth = 1000;
+        constexpr int estimatedTextHeight = 200;
+        constexpr int fontSize = 96;
+        window.draw_text({window.width() / 2 - estimatedTextWidth / 2, window.height() / 2 - estimatedTextHeight / 2}, "Good luck on the exam!", TDT4102::Color::white, fontSize, TDT4102::Font::arial_bold);
+        window.next_frame();
+    }
+}