diff --git a/src/cartoon.rs b/src/cartoon.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1e414ea4a3b06f93ee50f875d7165601ba44c1c0
--- /dev/null
+++ b/src/cartoon.rs
@@ -0,0 +1,160 @@
+use crate::fade_in::fade_in;
+use crate::slide::s1_title::Title;
+use crate::slide::s2_introduction::Introduction;
+use crate::slide::s3_complexity::Complexity;
+use crate::slide::s4_automata::Automata;
+use crate::slide::s5_fractals::Fractals;
+use crate::slide::s6_computation::Computation;
+use crate::slide::s7_mosaic::Mosaic;
+use crate::slide::s8_conclusion::Conclusion;
+use crate::slide::Slide;
+use eframe::egui::style::Margin;
+use eframe::egui::{Key, Style, TextStyle, Visuals};
+use eframe::epi::{Frame, Storage};
+use eframe::{egui, epi};
+
+/// Top-level state.
+pub struct Cartoon {
+    /// All slides (including their state).
+    slides: Vec<Box<dyn Slide>>,
+    /// Current index into [`slides`].
+    slide_index: usize,
+    /// When we started fading in the current slide, in seconds (with respect to `ctx.input().time`).
+    transition_time: f64,
+}
+
+// Default is used instead of a zero-argument constructor function, to be more idiomatic.
+impl Default for Cartoon {
+    fn default() -> Self {
+        Self {
+            slides: create_slides(),
+            slide_index: 0,
+            transition_time: 0.0,
+        }
+    }
+}
+
+/// Creates all the slides from default values. This will reset any and all animations and
+/// transitions built into the slides.
+///
+/// This is also how the chronology of the slideshow is determined.
+fn create_slides() -> Vec<Box<dyn Slide>> {
+    vec![
+        Box::new(Title::default()) as Box<dyn Slide>,
+        Box::new(Introduction::default()),
+        Box::new(Complexity::default()),
+        Box::new(Automata::default()),
+        Box::new(Fractals::default()),
+        Box::new(Computation::default()),
+        Box::new(Mosaic::default()),
+        Box::new(Conclusion::default()),
+    ]
+}
+
+impl epi::App for Cartoon {
+    /// The title of the window, which is mostly irrelevant.
+    fn name(&self) -> &str {
+        "Generative Art Cartoon"
+    }
+
+    /// Called once at window initialization.
+    fn setup(&mut self, ctx: &egui::Context, _frame: &Frame, _storage: Option<&dyn Storage>) {
+        // Top level style overrides.
+        let mut style = Style::default();
+        // This doesn't actually affect as many things as [`FadeIn`]'s fade duration.
+        style.animation_time = 0.5;
+        // Light theme.
+        style.visuals = Visuals::light();
+        // Larger margin for windows (which are used for more control when positing stuff).
+        style.spacing.window_margin = Margin::same(24.0);
+
+        // Increase font sizes a lot.
+        {
+            let header_style = style.text_styles.get_mut(&TextStyle::Heading).unwrap();
+            header_style.size = 48.0;
+        }
+        {
+            let body_style = style.text_styles.get_mut(&TextStyle::Body).unwrap();
+            body_style.size = 30.0;
+        }
+        {
+            let small_style = style.text_styles.get_mut(&TextStyle::Small).unwrap();
+            small_style.size = 22.0;
+        }
+        // TODO(finnb): This doesn't work. May have to fork syntax coloring code to fix it.
+        /*
+        {
+            let monospaced_style = style.text_styles.get_mut(&TextStyle::Monospace).unwrap();
+            monospaced_style.size = 22.0;
+        }
+         */
+        ctx.set_style(style);
+
+        // Start fading in title slide now.
+        self.transition_time = ctx.input().time;
+    }
+
+    /// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
+    fn update(&mut self, ctx: &egui::Context, _frame: &epi::Frame) {
+        // For inspiration and more examples, go to https://emilk.github.io/egui
+
+        /*
+        egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
+            egui::menu::bar(ui, |ui| {
+                ui.menu_button("File", |ui| {
+                    if ui.button("Quit").clicked() {
+                        frame.quit();
+                    }
+                });
+            });
+        });
+         */
+
+        // Go forward one slide (don't wait for intra-slide transitions).
+        //
+        // This will wrap around to the beginning.
+        let force_advance =
+            ctx.input().key_pressed(Key::ArrowRight) || ctx.input().key_pressed(Key::D);
+
+        // Play the next intra-slide transition or, if there isn't one, then go forward one slide.
+        //
+        // This will wrap around to the beginning.
+        let advance = force_advance || ctx.input().key_pressed(Key::Space);
+
+        // Go backward one slide (don't wait for intra-slide transitions).
+        //
+        // This will wrap around to the end.
+        let retreat = ctx.input().key_pressed(Key::ArrowLeft) || ctx.input().key_pressed(Key::A);
+
+        if advance || retreat {
+            self.slide_index = if advance {
+                if force_advance || self.slides[self.slide_index].transition(ctx) {
+                    let new = (self.slide_index + 1) % self.slides.len();
+                    if new == 0 {
+                        // We wrapped around to the beginning, so create slides to reset transitions.
+                        self.slides = create_slides();
+                    }
+                    self.transition_time = ctx.input().time;
+                    ctx.request_repaint();
+                    new
+                } else {
+                    self.slide_index
+                }
+            } else {
+                ctx.request_repaint();
+                self.transition_time = ctx.input().time;
+                self.slide_index
+                    .checked_sub(1)
+                    .unwrap_or(self.slides.len() - 1)
+            };
+        }
+
+        egui::CentralPanel::default().show(ctx, |ui| {
+            // The current slide may be fading in.
+            fade_in(ui, self.transition_time, |ui| {
+                // Render the current slide.
+                self.slides[self.slide_index].show(ui);
+            });
+        });
+    }
+}
diff --git a/src/color.rs b/src/color.rs
index 6e037eef4d555bd844b8c6a8ef07070afeeae38e..248cc49906dfbf36268dcff49f8d0b412dc76e1d 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -52,8 +52,6 @@ pub fn set_style_alpha_in_place(style: &mut Style, factor: f32) {
     set_alpha_in_place(&mut style.visuals.extreme_bg_color, factor);
     set_alpha_in_place(&mut style.visuals.code_bg_color, factor);
     set_alpha_in_place(&mut style.visuals.window_shadow.color, factor);
-
-    //style.visuals.widgets.noninteractive.bg_fill = Color32::TRANSPARENT;
 }
 
 /// Render some children with the specified alpha from 0 to 1.
diff --git a/src/component.rs b/src/component.rs
index cf13fbc60f9b148b4095e55287a5fbcd2518db68..6acd3eca01d62ea6201084b3b4cb7588aacf863c 100644
--- a/src/component.rs
+++ b/src/component.rs
@@ -1,3 +1,4 @@
-/// See component directory.
+/// See component directory. This file exists to make the individual components accessible to the
+/// rest of the code.
 pub mod code;
 pub mod grid;
diff --git a/src/component/code.rs b/src/component/code.rs
index 056e4362268265495776b13ca2718aca64c05066..4c6aebe1c4a8828ab36d2490427bdc61c4b36042 100644
--- a/src/component/code.rs
+++ b/src/component/code.rs
@@ -2,9 +2,10 @@ use crate::color::{set_alpha_in_place, set_style_alpha};
 use eframe::egui;
 use eframe::egui::{Align2, Color32, Context, Frame, Vec2, Widget, Window};
 
+/// A reusable (pseudo)code-display component.
 #[derive(Clone)]
 pub struct Code {
-    /// Must be unique.
+    /// Must be unique for `egui` reasons.
     pub name: &'static str,
     /// Horizontal offset from middle.
     pub offset_x: f32,
diff --git a/src/component/grid.rs b/src/component/grid.rs
index 499e410c4f0de09ec0bef45755bcefeb45931b58..a87c0a9ede2d61b1d785e55d80ab14ecdbad2601 100644
--- a/src/component/grid.rs
+++ b/src/component/grid.rs
@@ -4,9 +4,10 @@ use eframe::egui::{Align2, Color32, Context, Pos2, Rect, Rounding, Shape, Stroke
 use eframe::emath;
 use eframe::epaint::RectShape;
 
+/// A reusable grid component.
 #[derive(Clone)]
 pub struct Grid {
-    /// Must be unique.
+    /// Must be unique for `egui` reasons.
     pub name: &'static str,
     /// Horizontal offset from middle.
     pub offset_x: f32,
diff --git a/src/fade_in.rs b/src/fade_in.rs
index f028cab1ed8fe5e56edca3e8e1cd6fdc4037cd7d..045c19f441391fdbd49a5c4d841c6ad69a5edbc0 100644
--- a/src/fade_in.rs
+++ b/src/fade_in.rs
@@ -12,6 +12,7 @@ pub fn fade_in<R>(
     let since_transition = ui.ctx().input().time - fade_start;
     let alpha = (since_transition * (1.0 / FADE_DURATION)).min(1.0) as f32;
     if alpha < 1.0 {
+        // The fade isn't done yet, so schedule another render.
         ui.ctx().request_repaint();
     }
     with_alpha(ui, alpha, |ui| add_contents(ui))
diff --git a/src/governor.rs b/src/governor.rs
index d8078f695d4999e7f51041eb8c09260bd03e24c2..6d30419408cfd81b3100be6a266a4ffe380069fa 100644
--- a/src/governor.rs
+++ b/src/governor.rs
@@ -8,9 +8,11 @@ impl Governor {
     /// Is the rate limit ready to allow another action?
     pub fn ready(&mut self, time: f64, limit: f64) -> bool {
         if time > self.last + limit {
+            // Enough time passed.
             self.last = time;
             true
         } else {
+            // Keep waiting.
             false
         }
     }
diff --git a/src/image.rs b/src/image.rs
index 35c4819922698c79ca424e21a35ccdc007c8fe96..671258e5396b7234ba5d99cb00ce586f4e6a1576 100644
--- a/src/image.rs
+++ b/src/image.rs
@@ -19,7 +19,8 @@ macro_rules! ctx_img {
     }};
 }
 
-/// Decodes image data.
+/// Decodes image data. Formats other than PNG may require adding feature flags to the `image` crate
+/// in Cargo.toml.
 pub fn load_image_from_memory(
     image_data: &[u8],
 ) -> Result<eframe::egui::ColorImage, image::ImageError> {
diff --git a/src/main.rs b/src/main.rs
index 13ed9d8612680a69e3590f59996c6885aa2bc5ff..b74843f7adc3109005aa1a8313b8c1b08db16936 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,7 @@
 #![feature(derive_default_enum)]
 #![feature(mixed_integer_ops)]
 
+pub mod cartoon;
 pub mod color;
 pub mod component;
 pub mod fade_in;
@@ -8,23 +9,13 @@ pub mod governor;
 pub mod image;
 pub mod slide;
 
-use crate::fade_in::fade_in;
-use crate::slide::s1_title::Title;
-use crate::slide::s2_introduction::Introduction;
-use crate::slide::s3_complexity::Complexity;
-use crate::slide::s4_automata::Automata;
-use crate::slide::s5_fractals::Fractals;
-use crate::slide::s6_computation::Computation;
-use crate::slide::s7_mosaic::Mosaic;
-use crate::slide::s8_conclusion::Conclusion;
-use crate::slide::Slide;
+use crate::cartoon::Cartoon;
+use eframe::egui;
 use eframe::egui::style::Margin;
-use eframe::egui::{Key, Style, TextStyle, Visuals};
-use eframe::epi::{Frame, Storage};
-use eframe::{egui, epi};
 
 // Entry point.
 fn main() {
+    // cartoon.rs is a good place to look next.
     let app = Cartoon::default();
 
     // 16:9 aspect ratio.
@@ -46,145 +37,3 @@ fn main() {
 
     eframe::run_native(Box::new(app), native_options);
 }
-
-/// Top-level state.
-pub struct Cartoon {
-    /// All slides (including their state).
-    slides: Vec<Box<dyn Slide>>,
-    /// Current index into [`slides`].
-    slide_index: usize,
-    /// When we started fading in the current slide, in seconds (with respect to `ctx.input().time`).
-    transition_time: f64,
-}
-
-impl Default for Cartoon {
-    fn default() -> Self {
-        Self {
-            slides: create_slides(),
-            slide_index: 0,
-            transition_time: 0.0,
-        }
-    }
-}
-
-/// Creates all the slides from default values. This will reset any and all animations and
-/// transitions built into the slides.
-///
-/// This is also how the chronology of the slideshow is determined.
-fn create_slides() -> Vec<Box<dyn Slide>> {
-    vec![
-        Box::new(Title::default()) as Box<dyn Slide>,
-        Box::new(Introduction::default()),
-        Box::new(Complexity::default()),
-        Box::new(Automata::default()),
-        Box::new(Fractals::default()),
-        Box::new(Computation::default()),
-        Box::new(Mosaic::default()),
-        Box::new(Conclusion::default()),
-    ]
-}
-
-impl epi::App for Cartoon {
-    /// The title of the window, which is mostly irrelevant.
-    fn name(&self) -> &str {
-        "Generative Art Cartoon"
-    }
-
-    /// Called once at window initialization.
-    fn setup(&mut self, ctx: &egui::Context, _frame: &Frame, _storage: Option<&dyn Storage>) {
-        // Top level style overrides.
-        let mut style = Style::default();
-        style.animation_time = 0.5;
-        style.visuals = Visuals::light();
-        style.spacing.window_margin = Margin::same(24.0);
-
-        // Increase font sizes a lot.
-        {
-            let header_style = style.text_styles.get_mut(&TextStyle::Heading).unwrap();
-            header_style.size = 48.0;
-        }
-        {
-            let body_style = style.text_styles.get_mut(&TextStyle::Body).unwrap();
-            body_style.size = 30.0;
-        }
-        {
-            let small_style = style.text_styles.get_mut(&TextStyle::Small).unwrap();
-            small_style.size = 22.0;
-        }
-        // TODO(finnb): This doesn't work. May have to fork syntax coloring code to fix it.
-        /*
-        {
-            let monospaced_style = style.text_styles.get_mut(&TextStyle::Monospace).unwrap();
-            monospaced_style.size = 22.0;
-        }
-         */
-        ctx.set_style(style);
-
-        // Start fading in title slide now.
-        self.transition_time = ctx.input().time;
-    }
-
-    /// Put your widgets into a `SidePanel`, `TopPanel`, `CentralPanel`, `Window` or `Area`.
-    fn update(&mut self, ctx: &egui::Context, _frame: &epi::Frame) {
-        // For inspiration and more examples, go to https://emilk.github.io/egui
-
-        /*
-        egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
-            egui::menu::bar(ui, |ui| {
-                ui.menu_button("File", |ui| {
-                    if ui.button("Quit").clicked() {
-                        frame.quit();
-                    }
-                });
-            });
-        });
-         */
-
-        // Go forward one slide (don't wait for intra-slide transitions).
-        //
-        // This will wrap around to the beginning.
-        let force_advance =
-            ctx.input().key_pressed(Key::ArrowRight) || ctx.input().key_pressed(Key::D);
-
-        // Play the next intra-slide transition or, if there isn't one, then go forward one slide.
-        //
-        // This will wrap around to the beginning.
-        let advance = force_advance || ctx.input().key_pressed(Key::Space);
-
-        // Go backward one slide (don't wait for intra-slide transitions).
-        //
-        // This will wrap around to the end.
-        let retreat = ctx.input().key_pressed(Key::ArrowLeft) || ctx.input().key_pressed(Key::A);
-
-        if advance || retreat {
-            self.slide_index = if advance {
-                if force_advance || self.slides[self.slide_index].transition(ctx) {
-                    let new = (self.slide_index + 1) % self.slides.len();
-                    if new == 0 {
-                        // We wrapped around to the beginning, so create slides to reset transitions.
-                        self.slides = create_slides();
-                    }
-                    self.transition_time = ctx.input().time;
-                    ctx.request_repaint();
-                    new
-                } else {
-                    self.slide_index
-                }
-            } else {
-                ctx.request_repaint();
-                self.transition_time = ctx.input().time;
-                self.slide_index
-                    .checked_sub(1)
-                    .unwrap_or(self.slides.len() - 1)
-            };
-        }
-
-        egui::CentralPanel::default().show(ctx, |ui| {
-            // The current slide may be fading in.
-            fade_in(ui, self.transition_time, |ui| {
-                // Render the current slide.
-                self.slides[self.slide_index].show(ui);
-            });
-        });
-    }
-}
diff --git a/src/slide.rs b/src/slide.rs
index bc6977495e0441f598e718624145413aa144c109..43cfc7528d79e6d78cf379758145c9269827f4e3 100644
--- a/src/slide.rs
+++ b/src/slide.rs
@@ -1,5 +1,7 @@
 use eframe::egui::{Context, Ui};
 
+/// See slide directory. This file exists to make the individual slides accessible to the
+/// rest of the code.
 pub mod s1_title;
 pub mod s2_introduction;
 pub mod s3_complexity;
@@ -11,7 +13,7 @@ pub mod s8_conclusion;
 
 /// An interface for all slides.
 pub trait Slide {
-    /// Returns whether "done."
+    /// Returns whether "done" i.e. whether should switch to next slide.
     /// Repaint automatically requested.
     fn transition(&mut self, _ctx: &Context) -> bool {
         true
diff --git a/src/slide/s1_title.rs b/src/slide/s1_title.rs
index 8a983baafb570917aef3dab5819452fe1bf77514..ee88a527c9156e0279ea86d369d3bedc6d782d9a 100644
--- a/src/slide/s1_title.rs
+++ b/src/slide/s1_title.rs
@@ -1,5 +1,6 @@
 use crate::egui::Align2;
-use crate::{ctx_img, img, Margin, Slide};
+use crate::slide::Slide;
+use crate::{ctx_img, img, Margin};
 use eframe::egui;
 use eframe::egui::{Frame, Ui, Vec2, Window};
 
diff --git a/src/slide/s2_introduction.rs b/src/slide/s2_introduction.rs
index 6c476ea692cc2868c7f05b61f653cff670a29863..c24d1a02b00374eab4fa540f9f243a980ea64d2b 100644
--- a/src/slide/s2_introduction.rs
+++ b/src/slide/s2_introduction.rs
@@ -1,4 +1,6 @@
-use crate::{fade_in, Margin, Slide};
+use crate::fade_in::fade_in;
+use crate::slide::Slide;
+use eframe::egui::style::Margin;
 use eframe::egui::{Context, Frame, Ui};
 
 /// A slide that sets some expectations about generative art.
diff --git a/src/slide/s3_complexity.rs b/src/slide/s3_complexity.rs
index 3dd1dfa0e4b637b72c0c37fb93f5cdabbb98e915..794b0ab5f88f551d7106a2d3bd0b16dcd8ce5eb0 100644
--- a/src/slide/s3_complexity.rs
+++ b/src/slide/s3_complexity.rs
@@ -1,5 +1,5 @@
 use crate::egui::{Context, Ui, Window};
-use crate::Slide;
+use crate::slide::Slide;
 use eframe::egui::text_edit::{CCursorRange, TextEditState};
 use eframe::egui::{Align2, Frame, TextEdit, Vec2, Widget};
 use eframe::epaint::text::cursor::CCursor;
diff --git a/src/slide/s4_automata.rs b/src/slide/s4_automata.rs
index 4239c4bd015d5f9137e7fb26cce9a426099254de..6e83b65b78e0231f46b6625891abfa1f24f5bc57 100644
--- a/src/slide/s4_automata.rs
+++ b/src/slide/s4_automata.rs
@@ -1,6 +1,6 @@
 use crate::component::grid::Grid;
 use crate::governor::Governor;
-use crate::Slide;
+use crate::slide::Slide;
 use eframe::egui::style::Margin;
 use eframe::egui::{Color32, Frame, Ui};
 use rand::Rng;
diff --git a/src/slide/s5_fractals.rs b/src/slide/s5_fractals.rs
index adafb70dab72fdd5d6854c3787578edb7569f6d6..0d895ebba3916114bcf69d8bbc006382a6ce6ee7 100644
--- a/src/slide/s5_fractals.rs
+++ b/src/slide/s5_fractals.rs
@@ -8,7 +8,7 @@ use crate::egui::{Color32, Context, Frame, Ui};
 use crate::slide::s5_fractals::circle::circle;
 use crate::slide::s5_fractals::mandelbrot::mandelbrot;
 use crate::slide::s5_fractals::rectangle::rectangle;
-use crate::Slide;
+use crate::slide::Slide;
 use eframe::egui::style::Margin;
 
 /// Mandelbrot, etc.
diff --git a/src/slide/s5_fractals/README.md b/src/slide/s5_fractals/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..23ea144fa6649747bae8854d8f1116b449914614
--- /dev/null
+++ b/src/slide/s5_fractals/README.md
@@ -0,0 +1,4 @@
+These are linked into the program as both code (`use`) and data (`include_str!`). They should not necessarily obey Rust
+idioms, since they are adapted into pseudocode.
+
+In particular, use `return x;` syntax instead of `x`.
\ No newline at end of file
diff --git a/src/slide/s6_computation.rs b/src/slide/s6_computation.rs
index 68a4a05ae06eb683f1085966eeb9a8a9d5240cec..660de167366910b29afa928c0b52c9261e964041 100644
--- a/src/slide/s6_computation.rs
+++ b/src/slide/s6_computation.rs
@@ -1,5 +1,5 @@
 use crate::egui::{Frame, Ui};
-use crate::Slide;
+use crate::slide::Slide;
 use eframe::egui::style::Margin;
 
 #[derive(Default)]
diff --git a/src/slide/s7_mosaic.rs b/src/slide/s7_mosaic.rs
index e04e52257776f377626fa973d3be2cf8a02cf8a9..df7ff6871f6adc8cb8f2ade590e1189037f8c796 100644
--- a/src/slide/s7_mosaic.rs
+++ b/src/slide/s7_mosaic.rs
@@ -1,5 +1,5 @@
 use crate::egui::Ui;
-use crate::Slide;
+use crate::slide::Slide;
 use eframe::egui::style::Margin;
 use eframe::egui::Frame;
 
diff --git a/src/slide/s8_conclusion.rs b/src/slide/s8_conclusion.rs
index 169ff0a992b7153344e41820fa77a31cb3f6bb4c..6bb55e86c22fd850f85e4210fa2e0ff7a9294aee 100644
--- a/src/slide/s8_conclusion.rs
+++ b/src/slide/s8_conclusion.rs
@@ -1,5 +1,5 @@
 use crate::egui::Ui;
-use crate::Slide;
+use crate::slide::Slide;
 use eframe::egui::style::Margin;
 use eframe::egui::Frame;