diff --git a/src/component.rs b/src/component.rs
index 1140c84a90cdd26a06cb6e7d7dcccacd55c4aadf..c72f31f7cfb019ae37f5ffe13651b1ee6ed4fbec 100644
--- a/src/component.rs
+++ b/src/component.rs
@@ -4,4 +4,5 @@
 pub mod arrow;
 pub mod code;
 pub mod grid;
+pub mod image;
 pub mod triangle;
diff --git a/src/component/image.rs b/src/component/image.rs
new file mode 100644
index 0000000000000000000000000000000000000000..af14a04bc9d2fb280baed0a91c7ebd22b4e446a0
--- /dev/null
+++ b/src/component/image.rs
@@ -0,0 +1,71 @@
+use crate::color::{set_alpha, set_style_alpha};
+use crate::egui::{Color32, TextureHandle, Ui};
+use eframe::egui;
+use eframe::egui::style::Margin;
+use eframe::egui::{Frame, Pos2, Vec2, Widget, Window};
+
+/// A bitmap image i.e. an example of generative art.
+pub struct Image {
+    /// How many pixels of height the images should occupy.
+    pub height: f32,
+    /// How opaque the image is.
+    pub alpha: f32,
+    /// Center position.
+    pub position: Option<Pos2>,
+}
+
+impl Default for Image {
+    fn default() -> Self {
+        Self::new(128.0, 1.0, None)
+    }
+}
+
+impl Image {
+    pub fn new(height: f32, alpha: f32, position: Option<Pos2>) -> Self {
+        Self {
+            height,
+            alpha,
+            position,
+        }
+    }
+
+    /// Change the height of the image.
+    pub fn height(mut self, height: f32) -> Self {
+        self.height = height;
+        self
+    }
+
+    /// Change the alpha of the image.
+    pub fn alpha(mut self, alpha: f32) -> Self {
+        self.alpha = alpha;
+        self
+    }
+
+    /// Change the position of the image.
+    pub fn position(mut self, position: Pos2) -> Self {
+        // We assume that, if you call this method, you don't want [`None`].
+        self.position = Some(position);
+        self
+    }
+
+    pub fn show(&mut self, ui: &mut Ui, texture: &TextureHandle) {
+        let mut window = Window::new(texture.name())
+            .title_bar(false)
+            .resizable(false)
+            // Reduce margin of images.
+            .frame(
+                Frame::window(&set_style_alpha(ui.style(), self.alpha)).margin(Margin::same(5.0)),
+            );
+
+        if let Some(position) = self.position {
+            window = window.default_pos(position).fixed_pos(position);
+        }
+
+        window.show(ui.ctx(), |ui| {
+            // TODO: Support non-square images.
+            egui::Image::new(texture, Vec2::splat(self.height))
+                .tint(set_alpha(Color32::WHITE, self.alpha))
+                .ui(ui);
+        });
+    }
+}
diff --git a/src/fade_in.rs b/src/fade_in.rs
index 709ed89228d724937d66911a5f5656b4da121a3f..6778c6bf6a0354f8595d99a7b4d9a22181b53913 100644
--- a/src/fade_in.rs
+++ b/src/fade_in.rs
@@ -10,7 +10,7 @@ pub fn fade_in<R>(
     add_contents: impl FnOnce(&mut Ui) -> R,
 ) -> InnerResponse<R> {
     let since_transition = ui.ctx().input().time - fade_start;
-    let alpha = (since_transition * (1.0 / FADE_DURATION)).min(1.0) as f32;
+    let alpha = (since_transition * (1.0 / FADE_DURATION)).clamp(0.0, 1.0) as f32;
     if alpha < 1.0 {
         // The fade isn't done yet, so schedule another render.
         ui.ctx().request_repaint();
diff --git a/src/image/fluid0.png b/src/image/fluid0.png
new file mode 100644
index 0000000000000000000000000000000000000000..2ee8ff33575215fc0333c3ebe47d19235a84e2fc
Binary files /dev/null and b/src/image/fluid0.png differ
diff --git a/src/image/raytracing0.png b/src/image/raytracing0.png
new file mode 100644
index 0000000000000000000000000000000000000000..b3d95a395260668a3dadb117463ba56d6571fd7b
Binary files /dev/null and b/src/image/raytracing0.png differ
diff --git a/src/slide/s1_title.rs b/src/slide/s1_title.rs
index 9e4c09715eb32fee5774ff58069c263459d233d7..17639e20d514782a8bedf535a7c2fe8d6f74ebee 100644
--- a/src/slide/s1_title.rs
+++ b/src/slide/s1_title.rs
@@ -1,3 +1,4 @@
+use crate::component::image::Image;
 use crate::egui::{Align2, Context};
 use crate::fade_in::fade_in;
 use crate::slide::Slide;
@@ -47,14 +48,7 @@ impl Slide for Title {
         }
 
         for example in &self.examples {
-            Window::new(example.name())
-                .title_bar(false)
-                .resizable(false)
-                // Reduce margin of example images.
-                .frame(Frame::window(&ui.style()).margin(Margin::same(5.0)))
-                .show(ui.ctx(), |ui| {
-                    ui.image(example, Vec2::splat(128.0));
-                });
+            Image::default().show(ui, example);
         }
 
         if let &TitleState::Title { fade_start } = &self.state {
diff --git a/src/slide/s4_automata.rs b/src/slide/s4_automata.rs
index 292855c7fd5df390356299d988ec66468ef516fb..e0551c36a4110b0dedc1ac4a2cdf494b053659b2 100644
--- a/src/slide/s4_automata.rs
+++ b/src/slide/s4_automata.rs
@@ -1,6 +1,7 @@
 use crate::color::set_alpha;
 use crate::component::arrow::Arrow;
 use crate::component::grid::Grid;
+use crate::component::image::Image;
 use crate::ctx_img;
 use crate::egui::{Align2, Context, TextureHandle, Vec2, Window};
 use crate::fade_in::{fade_in, fade_in_manual};
@@ -119,17 +120,11 @@ impl Slide for Automata {
         });
 
         // TODO: Fade/move/position as appropriate.
-        Window::new("portrait")
-            .title_bar(false)
-            .resizable(false)
-            .frame(Frame::window(&ui.style()).margin(Margin::same(5.0)))
-            .show(ui.ctx(), |ui| {
-                ui.image(
-                    self.conway_portrait
-                        .get_or_insert_with(|| ctx_img!(ui.ctx(), "conway_portrait.png")),
-                    Vec2::splat(128.0),
-                );
-            });
+        Image::default().show(
+            ui,
+            self.conway_portrait
+                .get_or_insert_with(|| ctx_img!(ui.ctx(), "conway_portrait.png")),
+        );
 
         // Need to continuously animate the grid, or at least poll the governor.
         ui.ctx().request_repaint();
@@ -156,7 +151,7 @@ impl Slide for Automata {
             } => {
                 // Iterate grid ~5 times a second.
                 if self.governor.ready(ui.ctx().input().time, 0.2) {
-                    // Schedule some special events at some interation counts.
+                    // Schedule some special events at some iteration counts.
                     match *iterations {
                         10 => {
                             // Automatically begin expansion.
@@ -201,7 +196,9 @@ impl Slide for Automata {
         }
 
         if let Some(rules_fade) = rules_fade {
-            fade_in(ui, rules_fade, |ui| {
+            // Give some time for the grid to slide left.
+            const RULES_DELAY: f64 = 0.3;
+            fade_in(ui, rules_fade + RULES_DELAY, |ui| {
                 Window::new("conway_rules")
                     .frame(Frame::window(&ui.style()))
                     .anchor(Align2::CENTER_CENTER, Vec2::new(HORIZONTAL_OFFSET, 0.0))
@@ -209,7 +206,7 @@ impl Slide for Automata {
                     .resizable(false)
                     .show(ui.ctx(), |ui| {
                         // Nested fade in since fade doesn't propagate from [`Window`] to children.
-                        fade_in(ui, rules_fade, |ui| {
+                        fade_in(ui, rules_fade + RULES_DELAY, |ui| {
                             ui.label(" ⏵ Cells with fewer than two neighbors die");
                             ui.label(" ⏵ Cells with more than three neighbors die");
                             ui.label(" ⏵ Cells with three neighbors become alive");
diff --git a/src/slide/s6_computation.rs b/src/slide/s6_computation.rs
index 660de167366910b29afa928c0b52c9261e964041..02c78bb007e4d967b985adb871717c16f2a5910d 100644
--- a/src/slide/s6_computation.rs
+++ b/src/slide/s6_computation.rs
@@ -1,19 +1,87 @@
-use crate::egui::{Frame, Ui};
+use crate::component::image::Image;
+use crate::ctx_img;
+use crate::egui::{Context, Frame, Ui};
+use crate::fade_in::{fade_in, fade_in_manual};
 use crate::slide::Slide;
 use eframe::egui::style::Margin;
+use eframe::egui::{Pos2, TextureHandle};
 
 #[derive(Default)]
-pub struct Computation {}
+pub struct Computation {
+    /// Will fade in one by one.
+    examples: Vec<ComputationExample>,
+}
+
+/// One bullet point and texture combination.
+struct ComputationExample {
+    /// Bullet point text.
+    label: &'static str,
+    /// Texture handle to render on the right side.
+    texture: TextureHandle,
+    /// When we started fading it ([`None`] if we haven't started yet).
+    fade_start: Option<f64>,
+}
+
+impl ComputationExample {
+    pub fn new(label: &'static str, texture: TextureHandle) -> Self {
+        Self {
+            label,
+            texture,
+            fade_start: None,
+        }
+    }
+}
 
 impl Slide for Computation {
+    fn transition(&mut self, ctx: &Context) -> bool {
+        for example in &mut self.examples {
+            if example.fade_start.is_none() {
+                // If any image has not yet started fading in, fade it in and don't go to next slide.
+                example.fade_start = Some(ctx.input().time);
+                return false;
+            }
+        }
+        true
+    }
+
     fn show(&mut self, ui: &mut Ui) {
+        if self.examples.is_empty() {
+            // For now, these images are somewhat like placeholders.
+            self.examples = vec![
+                ComputationExample::new("Raytracing", ctx_img!(ui.ctx(), "raytracing0.png")),
+                ComputationExample::new("Raymarching", ctx_img!(ui.ctx(), "raymarching1.png")),
+                ComputationExample::new("Particle simulation", ctx_img!(ui.ctx(), "atom0.png")),
+                ComputationExample::new("Fluid simulation", ctx_img!(ui.ctx(), "fluid0.png")),
+            ]
+        }
+
+        const IMAGE_SCALE: f32 = 256.0;
+        let window_width = ui.available_width();
+        let window_height = ui.available_height();
+        let window_width_per_image = window_width / self.examples.len() as f32;
+
         Frame::none().margin(Margin::same(20.0)).show(ui, |ui| {
             ui.heading("More Computation-based Art");
             ui.add_space(8.0);
-            ui.label(" ⏵ Raytracing");
-            ui.label(" ⏵ Raymarching");
-            ui.label(" ⏵ Particle simulations");
-            ui.label(" ⏵ Fluid simulations");
+            for (i, example) in self.examples.iter().enumerate() {
+                if let Some(fade_start) = example.fade_start {
+                    fade_in(ui, fade_start, |ui| {
+                        ui.label(format!(" ⏵ {}", example.label));
+                    });
+                    fade_in_manual(ui, fade_start, |ui, alpha| {
+                        let position = Pos2::new(
+                            (window_width_per_image - IMAGE_SCALE) * 0.5
+                                + window_width * (i as f32) / self.examples.len() as f32,
+                            window_height - IMAGE_SCALE * 1.1,
+                        );
+                        Image::default()
+                            .height(IMAGE_SCALE)
+                            .position(position)
+                            .alpha(alpha)
+                            .show(ui, &example.texture);
+                    });
+                }
+            }
         });
     }
 }