diff --git a/src/main.rs b/src/main.rs
index b05255ab65cc7c715d1874f584d3e0e775b956a3..eb5de002219acb4469a36d728bbdc289bc7e5961 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,5 @@
+#![feature(derive_default_enum)]
+
 pub mod image;
 pub mod slide;
 
@@ -43,16 +45,20 @@ pub struct Cartoon {
 impl Default for Cartoon {
     fn default() -> Self {
         Self {
-            slides: vec![
-                Box::new(Title::default()) as Box<dyn Slide>,
-                Box::new(Introduction::default()),
-                Box::new(Complexity::default()),
-            ],
+            slides: create_slides(),
             slide_index: 0,
         }
     }
 }
 
+fn create_slides() -> Vec<Box<dyn Slide>> {
+    vec![
+        Box::new(Title::default()) as Box<dyn Slide>,
+        Box::new(Introduction::default()),
+        Box::new(Complexity::default()),
+    ]
+}
+
 impl epi::App for Cartoon {
     fn name(&self) -> &str {
         "Generative Art Cartoon"
@@ -94,8 +100,27 @@ impl epi::App for Cartoon {
         });
          */
 
-        if ctx.input().key_pressed(Key::Space) {
-            self.slide_index = (self.slide_index + 1) % self.slides.len();
+        let advance = ctx.input().key_pressed(Key::Space)
+            || ctx.input().key_pressed(Key::ArrowRight)
+            || ctx.input().key_pressed(Key::D);
+        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) {
+                    let new = (self.slide_index + 1) % self.slides.len();
+                    if new == 0 {
+                        self.slides = create_slides();
+                    }
+                    ctx.request_repaint();
+                    new
+                } else {
+                    self.slide_index
+                }
+            } else {
+                ctx.request_repaint();
+                self.slide_index.saturating_sub(1)
+            };
         }
 
         egui::CentralPanel::default().show(ctx, |ui| {
diff --git a/src/slide.rs b/src/slide.rs
index 4339fee53071380718b755ad4369203f927fc2c7..123ae606f4f0538e4b98ecb17929f5352f386654 100644
--- a/src/slide.rs
+++ b/src/slide.rs
@@ -7,4 +7,9 @@ pub mod s3_complexity;
 
 pub trait Slide {
     fn show(&mut self, ui: &mut Ui, ctx: &Context);
+    /// Returns whether "done."
+    /// Repaint automatically requested.
+    fn transition(&mut self, ctx: &Context) -> bool {
+        true
+    }
 }
diff --git a/src/slide/s3_complexity.rs b/src/slide/s3_complexity.rs
index 15b3a16d7b5b853bd9949b1f5daa4c9e77cb90d1..9886bd0df577d0fa8b739e39a1c68d991ca7a6e2 100644
--- a/src/slide/s3_complexity.rs
+++ b/src/slide/s3_complexity.rs
@@ -3,11 +3,33 @@ use crate::{Key, Slide};
 use eframe::egui::text_edit::{CCursorRange, CursorRange, TextEditState};
 use eframe::egui::{Align, Align2, Frame, Layout, TextEdit, Vec2};
 use eframe::epaint::text::cursor::{CCursor, Cursor};
+use std::ops::RangeInclusive;
 
 #[derive(Default)]
-pub struct Complexity {}
+pub struct Complexity {
+    state: ComplexityState,
+    last_time: f64,
+}
+
+#[derive(Default, Eq, PartialEq, Debug)]
+enum ComplexityState {
+    #[default]
+    Before,
+    Selecting(usize),
+    Typing(usize),
+    After,
+}
 
 impl Slide for Complexity {
+    fn transition(&mut self, ctx: &Context) -> bool {
+        if self.state == ComplexityState::Before {
+            self.state = ComplexityState::Selecting(0);
+            false
+        } else {
+            true
+        }
+    }
+
     fn show(&mut self, ui: &mut Ui, ctx: &Context) {
         Window::new("complexity")
             .title_bar(false)
@@ -16,15 +38,56 @@ impl Slide for Complexity {
             .anchor(Align2::CENTER_CENTER, Vec2::ZERO)
             .fixed_size(Vec2::new(440.0, 100.0))
             .show(ctx, |ui| {
+                ui.ctx().request_repaint();
+                let time = ui.input().time;
+                let time_delta = (time - self.last_time).min(1.0);
+                let made_progress = time_delta > 0.1;
+                if made_progress {
+                    self.last_time = time;
+                }
+
                 ui.vertical_centered_justified(|ui| {
                     let mut state = TextEditState::default();
-                    state.set_ccursor_range(Some(CCursorRange::two(
-                        CCursor::new(0),
-                        CCursor::new(7),
-                    )));
-                    let text_edit =
-                        ui.text_edit_singleline(&mut "Complex Rules âž¡ Complex Results?");
-                    text_edit.request_focus();
+                    let text;
+                    match &mut self.state {
+                        ComplexityState::Before => {
+                            text = String::from("Complex Rules âž¡ Complex Results?");
+                        }
+                        ComplexityState::Selecting(progress) => {
+                            text = String::from("Complex Rules âž¡ Complex Results?");
+                            state.set_ccursor_range(Some(CCursorRange::two(
+                                CCursor::new(0),
+                                CCursor::new(*progress),
+                            )));
+                            if made_progress {
+                                if *progress == "Complex".len() {
+                                    self.state = ComplexityState::Typing(0);
+                                } else {
+                                    *progress += 1;
+                                }
+                            }
+                        }
+                        ComplexityState::Typing(progress) => {
+                            text = format!(" {} Rules âž¡ Complex Results?", &"Simple"[0..*progress]);
+                            if made_progress {
+                                if *progress == "Simple".len() {
+                                    self.state = ComplexityState::After;
+                                } else {
+                                    *progress += 1;
+                                }
+                            }
+                        }
+                        ComplexityState::After => {
+                            text = String::from(" Simple Rules âž¡ Complex Results!");
+                        }
+                    }
+
+                    let text_edit = ui.text_edit_singleline(&mut text.as_str());
+                    if state.ccursor_range().is_some() {
+                        text_edit.request_focus();
+                    } else {
+                        text_edit.surrender_focus();
+                    }
                     TextEdit::store_state(ctx, text_edit.id, state);
                 });
             });