diff --git a/src/component/grid.rs b/src/component/grid.rs
index fec1d980060699f00fea67596982fa3ebfbc0c51..229157e5c2c7db9f8787e112d0272a4c3ab13b74 100644
--- a/src/component/grid.rs
+++ b/src/component/grid.rs
@@ -1,13 +1,20 @@
-use eframe::egui::{Align2, Color32, Context, Pos2, Rect, Shape, Stroke, Ui, Vec2, Window};
+use eframe::egui::{
+    Align2, Color32, Context, Pos2, Rect, Rounding, Shape, Stroke, Ui, Vec2, Window,
+};
 use eframe::emath;
+use eframe::epaint::RectShape;
 
+#[derive(Clone)]
 pub struct Grid {
-    name: &'static str,
-    offset_x: f32,
-    offset_y: f32,
-    size_cells: usize,
-    size_pixels: f32,
-    stroke: Color32,
+    pub name: &'static str,
+    pub offset_x: f32,
+    pub offset_y: f32,
+    pub size_cells: usize,
+    pub size_pixels: f32,
+    pub stroke: Color32,
+    pub stroke_width: f32,
+    pub background: Color32,
+    fill: Vec<Color32>,
 }
 
 impl Default for Grid {
@@ -19,11 +26,44 @@ impl Default for Grid {
             size_cells: 16,
             size_pixels: 400.0,
             stroke: Color32::GRAY,
+            stroke_width: 1.0,
+            background: Color32::from_rgb(230, 230, 230),
+            fill: Vec::new(),
         }
     }
 }
 
 impl Grid {
+    fn index(&self, x: usize, y: usize) -> usize {
+        assert!(x < self.size_cells);
+        assert!(y < self.size_cells);
+        x + y * self.size_cells
+    }
+
+    pub fn fill(&self, x: usize, y: usize) -> Color32 {
+        self.fill
+            .get(self.index(x, y))
+            .cloned()
+            .unwrap_or(Color32::TRANSPARENT)
+    }
+
+    pub fn set_fill(&mut self, x: usize, y: usize, fill: Color32) {
+        let idx = self.index(x, y);
+        loop {
+            match self.fill.get_mut(idx) {
+                Some(f) => {
+                    *f = fill;
+                    return;
+                }
+                None => self.fill.push(Color32::TRANSPARENT),
+            }
+        }
+    }
+
+    pub fn clear_fill(&mut self) {
+        self.fill.clear();
+    }
+
     pub fn show(&self, ctx: &Context) {
         Window::new(self.name)
             .title_bar(false)
@@ -37,27 +77,55 @@ impl Grid {
                     rect,
                 );
 
-                for c in 0..=self.size_cells {
-                    let coord = c as f32 / self.size_cells as f32;
-                    // Horizontal.
-                    ui.painter().add(Shape::LineSegment {
-                        points: [
-                            to_screen * Pos2::new(0.0, coord),
-                            to_screen * Pos2::new(1.0, coord),
-                        ],
-                        stroke: Stroke::new(1.0, self.stroke),
-                    });
-                    // Vertical.
-                    ui.painter().add(Shape::LineSegment {
-                        points: [
-                            to_screen * Pos2::new(coord, 0.0),
-                            to_screen * Pos2::new(coord, 1.0),
-                        ],
-                        stroke: Stroke::new(1.0, self.stroke),
-                    });
+                if self.background.a() > 0 {
+                    ui.painter().add(Shape::Rect(RectShape::filled(
+                        to_screen.transform_rect(Rect::from_x_y_ranges(0f32..=1f32, 0f32..=1f32)),
+                        Rounding::none(),
+                        self.background,
+                    )));
                 }
-                for x in 0..=self.size_cells {
-                    for y in 0..=self.size_cells {}
+
+                let cell_width = 1.0 / self.size_cells as f32;
+                for y in 0..self.size_cells {
+                    let y_coord = y as f32 / self.size_cells as f32;
+                    for x in 0..self.size_cells {
+                        let idx = self.index(x, y);
+                        if let Some(fill) = self.fill.get(idx) {
+                            if fill.a() > 0 {
+                                let x_coord = x as f32 / self.size_cells as f32;
+                                ui.painter().add(Shape::Rect(RectShape::filled(
+                                    to_screen.transform_rect(Rect::from_x_y_ranges(
+                                        x_coord..=x_coord + cell_width,
+                                        y_coord..=y_coord + cell_width,
+                                    )),
+                                    Rounding::none(),
+                                    *fill,
+                                )));
+                            }
+                        }
+                    }
+                }
+
+                if self.stroke.a() > 0 && self.stroke_width > 0.0 {
+                    for c in 0..=self.size_cells {
+                        let coord = c as f32 / self.size_cells as f32;
+                        // Horizontal.
+                        ui.painter().add(Shape::LineSegment {
+                            points: [
+                                to_screen * Pos2::new(0.0, coord),
+                                to_screen * Pos2::new(1.0, coord),
+                            ],
+                            stroke: Stroke::new(self.stroke_width, self.stroke),
+                        });
+                        // Vertical.
+                        ui.painter().add(Shape::LineSegment {
+                            points: [
+                                to_screen * Pos2::new(coord, 0.0),
+                                to_screen * Pos2::new(coord, 1.0),
+                            ],
+                            stroke: Stroke::new(self.stroke_width, self.stroke),
+                        });
+                    }
                 }
             });
     }
diff --git a/src/governor.rs b/src/governor.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e9b8c6229c255e8d519a4f13b12a0f950586696d
--- /dev/null
+++ b/src/governor.rs
@@ -0,0 +1,15 @@
+#[derive(Default)]
+pub struct Governor {
+    last: f64,
+}
+
+impl Governor {
+    pub fn ready(&mut self, time: f64, limit: f64) -> bool {
+        if time > self.last + limit {
+            self.last = time;
+            true
+        } else {
+            false
+        }
+    }
+}
diff --git a/src/main.rs b/src/main.rs
index 6acc4a6adaf67458e70301baeb6b002ebf564d3d..e37c8908ebb7ba53c498d75ae89da5825cd0cb23 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,8 @@
 #![feature(derive_default_enum)]
+#![feature(mixed_integer_ops)]
 
 pub mod component;
+pub mod governor;
 pub mod image;
 pub mod slide;
 
@@ -103,14 +105,14 @@ impl epi::App for Cartoon {
         });
          */
 
-        let advance = ctx.input().key_pressed(Key::Space)
-            || ctx.input().key_pressed(Key::ArrowRight)
-            || ctx.input().key_pressed(Key::D);
+        let force_advance =
+            ctx.input().key_pressed(Key::ArrowRight) || ctx.input().key_pressed(Key::D);
+        let advance = force_advance || ctx.input().key_pressed(Key::Space);
         let retreat = ctx.input().key_pressed(Key::ArrowLeft) || ctx.input().key_pressed(Key::A);
 
         if advance || retreat {
             self.slide_index = if advance {
-                if self.slides[self.slide_index].transition(ctx) {
+                if force_advance || self.slides[self.slide_index].transition(ctx) {
                     let new = (self.slide_index + 1) % self.slides.len();
                     if new == 0 {
                         self.slides = create_slides();
diff --git a/src/slide/s2_introduction.rs b/src/slide/s2_introduction.rs
index 5fbfed346b4c40b6bdd8bd2eb4185dc2e3e485c5..ca13b1139a429422850fdfcaae378022678ac1cc 100644
--- a/src/slide/s2_introduction.rs
+++ b/src/slide/s2_introduction.rs
@@ -3,19 +3,33 @@ use eframe::egui::{Context, Frame, Ui};
 use eframe::{egui, epi};
 
 #[derive(Default)]
-pub struct Introduction {}
+pub struct Introduction {
+    strengths: bool,
+}
 
 impl Slide for Introduction {
+    fn transition(&mut self, ctx: &Context) -> bool {
+        let done = self.strengths;
+        self.strengths = true;
+        done
+    }
+
     fn show(&mut self, ui: &mut Ui, _ctx: &Context) {
         Frame::none().margin(Margin::same(20.0)).show(ui, |ui| {
-            ui.heading("Introduction");
+            ui.heading("Introduction to Artistic Algorithms");
             ui.add_space(8.0);
             ui.label("Weaknesses");
-            ui.small(" ✖ Blah");
-            ui.small(" ✖ Blah");
-            ui.add_space(10.0);
-            ui.label("Strengths");
-            ui.small(" ✔ Blah");
+            ui.small(" ✖ Social context");
+            ui.small(" ✖ Human emotion");
+            ui.small(" ✖ Political commentary");
+            if self.strengths {
+                ui.add_space(10.0);
+                ui.label("Strengths");
+                ui.small(" ✔ Following rules");
+                ui.small(" ✔ Performing computation");
+                ui.small(" ✔ Harnessing chaos and randomness");
+                ui.small(" ✔ Guided exploration");
+            }
         });
     }
 }
diff --git a/src/slide/s4_conway.rs b/src/slide/s4_conway.rs
index a3bb41ff71b2a02b1f8149b6279704114e045a3b..2e2babcef23c0c0ccfecbf1c11e8c622e4502a09 100644
--- a/src/slide/s4_conway.rs
+++ b/src/slide/s4_conway.rs
@@ -1,15 +1,101 @@
 use crate::component::grid::Grid;
+use crate::governor::Governor;
 use crate::Slide;
+use eframe::egui::style::Margin;
 use eframe::egui::{Align2, Color32, Context, Frame, Pos2, Rect, Shape, Stroke, Ui, Vec2, Window};
 use eframe::emath;
+use rand::Rng;
 
-#[derive(Default)]
 pub struct Conway {
     life: Grid,
+    governor: Governor,
+}
+
+impl Default for Conway {
+    fn default() -> Self {
+        let mut life = Grid::default();
+
+        for y in 0..life.size_cells {
+            for x in 0..life.size_cells {
+                if rand::thread_rng().gen_bool(0.5) {
+                    life.set_fill(x, y, Color32::BLACK);
+                }
+            }
+        }
+
+        /*
+        life.clear_fill();
+        life.set_fill(5, 5, Color32::BLACK);
+        life.set_fill(6, 5, Color32::BLACK);
+        life.set_fill(7, 5, Color32::BLACK);
+         */
+
+        Self {
+            life,
+            governor: Governor::default(),
+        }
+    }
 }
 
 impl Slide for Conway {
     fn show(&mut self, ui: &mut Ui, ctx: &Context) {
+        Frame::none().margin(Margin::same(20.0)).show(ui, |ui| {
+            ui.vertical_centered(|ui| {
+                ui.heading("Cellular Automata");
+            });
+        });
+
+        ctx.request_repaint();
+        if self.governor.ready(ctx.input().time, 0.2) {
+            self.life = conway(&self.life);
+        }
         self.life.show(ctx);
     }
 }
+
+fn conway(grid: &Grid) -> Grid {
+    let mut new = grid.clone();
+    new.clear_fill();
+
+    for cy in 0..grid.size_cells {
+        for cx in 0..grid.size_cells {
+            let mut neighbors = 0;
+            for dy in -1..=1 {
+                for dx in -1..=1 {
+                    let (x, y) = match (cx.checked_add_signed(dx), cy.checked_add_signed(dy)) {
+                        (Some(x), Some(y)) if x < grid.size_cells && y < grid.size_cells => (x, y),
+                        _ => continue,
+                    };
+                    if x == cx && y == cy {
+                        // Don't consider self.
+                        continue;
+                    }
+                    if grid.fill(x, y).a() > 0 {
+                        neighbors += 1;
+                    }
+                }
+            }
+
+            let mut alive = grid.fill(cx, cy).a() > 0;
+
+            alive = match (alive, neighbors) {
+                (false, 3) => true,
+                (false, _) => false,
+                (true, 2) | (true, 3) => true,
+                (true, _) => false,
+            };
+
+            new.set_fill(
+                cx,
+                cy,
+                if alive {
+                    Color32::BLACK
+                } else {
+                    Color32::TRANSPARENT
+                },
+            );
+        }
+    }
+
+    new
+}