diff --git a/src/component.rs b/src/component.rs
new file mode 100644
index 0000000000000000000000000000000000000000..f7a1b46d277d4650c443e92631f5e09ed5c21b10
--- /dev/null
+++ b/src/component.rs
@@ -0,0 +1 @@
+pub mod grid;
diff --git a/src/component/grid.rs b/src/component/grid.rs
new file mode 100644
index 0000000000000000000000000000000000000000..fec1d980060699f00fea67596982fa3ebfbc0c51
--- /dev/null
+++ b/src/component/grid.rs
@@ -0,0 +1,64 @@
+use eframe::egui::{Align2, Color32, Context, Pos2, Rect, Shape, Stroke, Ui, Vec2, Window};
+use eframe::emath;
+
+pub struct Grid {
+    name: &'static str,
+    offset_x: f32,
+    offset_y: f32,
+    size_cells: usize,
+    size_pixels: f32,
+    stroke: Color32,
+}
+
+impl Default for Grid {
+    fn default() -> Self {
+        Self {
+            name: "grid",
+            offset_x: 0.0,
+            offset_y: 0.0,
+            size_cells: 16,
+            size_pixels: 400.0,
+            stroke: Color32::GRAY,
+        }
+    }
+}
+
+impl Grid {
+    pub fn show(&self, ctx: &Context) {
+        Window::new(self.name)
+            .title_bar(false)
+            //.frame(Frame::none())
+            .anchor(Align2::CENTER_CENTER, Vec2::ZERO)
+            .fixed_size(Vec2::splat(self.size_pixels))
+            .show(ctx, |ui| {
+                let (_id, rect) = ui.allocate_space(Vec2::splat(self.size_pixels));
+                let to_screen = emath::RectTransform::from_to(
+                    Rect::from_x_y_ranges(0.0..=1.0, 0.0..=1.0),
+                    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),
+                    });
+                }
+                for x in 0..=self.size_cells {
+                    for y in 0..=self.size_cells {}
+                }
+            });
+    }
+}
diff --git a/src/main.rs b/src/main.rs
index 770b07b71b41375d28184f0e95f25332e80c1f4f..6acc4a6adaf67458e70301baeb6b002ebf564d3d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,5 +1,6 @@
 #![feature(derive_default_enum)]
 
+pub mod component;
 pub mod image;
 pub mod slide;
 
diff --git a/src/slide/s4_conway.rs b/src/slide/s4_conway.rs
index 781e2d498351228018c7fa2f9d027fccd9f4c180..a3bb41ff71b2a02b1f8149b6279704114e045a3b 100644
--- a/src/slide/s4_conway.rs
+++ b/src/slide/s4_conway.rs
@@ -1,51 +1,15 @@
+use crate::component::grid::Grid;
 use crate::Slide;
-use eframe::egui::{
-    Align2, Color32, Context, Frame, Grid, Pos2, Rect, Shape, Stroke, Ui, Vec2, Window,
-};
+use eframe::egui::{Align2, Color32, Context, Frame, Pos2, Rect, Shape, Stroke, Ui, Vec2, Window};
 use eframe::emath;
 
 #[derive(Default)]
-pub struct Conway {}
+pub struct Conway {
+    life: Grid,
+}
 
 impl Slide for Conway {
     fn show(&mut self, ui: &mut Ui, ctx: &Context) {
-        let PIXELS: f32 = 400.0;
-        let CELLS: usize = 32;
-
-        Window::new("conway")
-            .title_bar(false)
-            //.frame(Frame::none())
-            .anchor(Align2::CENTER_CENTER, Vec2::ZERO)
-            .fixed_size(Vec2::splat(PIXELS))
-            .show(ctx, |ui| {
-                let (_id, rect) = ui.allocate_space(Vec2::splat(PIXELS));
-                let to_screen = emath::RectTransform::from_to(
-                    Rect::from_x_y_ranges(0.0..=1.0, 0.0..=1.0),
-                    rect,
-                );
-
-                for c in 0..=CELLS {
-                    let coord = c as f32 / 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, Color32::BLACK),
-                    });
-                    // 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, Color32::GRAY),
-                    });
-                }
-                for x in 0..=CELLS {
-                    for y in 0..=CELLS {}
-                }
-            });
+        self.life.show(ctx);
     }
 }