diff --git a/Cargo.lock b/Cargo.lock
index 7c8ecbb..0b0fa8b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -142,6 +142,15 @@ dependencies = [
  "uguid",
 ]
 
+[[package]]
+name = "uefi-gol"
+version = "0.1.0"
+dependencies = [
+ "heapless",
+ "log",
+ "uefi",
+]
+
 [[package]]
 name = "uefi-macros"
 version = "0.17.0"
@@ -164,15 +173,6 @@ dependencies = [
  "uguid",
 ]
 
-[[package]]
-name = "uefi-textadventure"
-version = "0.1.0"
-dependencies = [
- "heapless",
- "log",
- "uefi",
-]
-
 [[package]]
 name = "uguid"
 version = "2.2.0"
diff --git a/Cargo.toml b/Cargo.toml
index f23a761..f4eee96 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,5 @@
 [package]
-name = "uefi-textadventure"
+name = "uefi-gol"
 version = "0.1.0"
 edition = "2021"
 
diff --git a/run.sh b/run.sh
index 566dee0..84b5a87 100755
--- a/run.sh
+++ b/run.sh
@@ -5,7 +5,7 @@ set -e
 
 cargo build --target x86_64-unknown-uefi
 mkdir -p esp/efi/boot
-cp target/x86_64-unknown-uefi/debug/uefi-textadventure.efi esp/efi/boot/bootx64.efi
+cp target/x86_64-unknown-uefi/debug/uefi-gol.efi esp/efi/boot/bootx64.efi
 qemu-system-x86_64 -enable-kvm \
     -drive if=pflash,format=raw,readonly=on,file=edk2-x86_64-code.fd \
     -drive format=raw,file=fat:rw:esp
diff --git a/src/main.rs b/src/main.rs
index f956fd7..97c5cee 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,95 +1,151 @@
 #![no_main]
 #![no_std]
 
-use core::{fmt::Write, iter::repeat};
+use core::{
+    fmt::Write,
+    iter::repeat,
+    ops::{Deref, DerefMut},
+};
 
 use heapless::{String, Vec};
-use log::info;
 use system::with_stdout;
 use uefi::prelude::*;
 
+const WIDTH: usize = 30;
+const HEIGHT: usize = 30;
+
+type Matrix = Vec<Vec<bool, WIDTH>, HEIGHT>;
+
+#[derive(Debug, Clone)]
+struct Life {
+    matrix: Matrix,
+}
+
+impl Deref for Life {
+    type Target = Matrix;
+
+    fn deref(&self) -> &Self::Target {
+        &self.matrix
+    }
+}
+
+impl Default for Life {
+    fn default() -> Self {
+        let mut matrix: Vec<Vec<bool, WIDTH>, HEIGHT> = Vec::default();
+        for _ in 0..HEIGHT {
+            matrix.push(repeat(false).take(WIDTH).collect()).unwrap();
+        }
+        Life { matrix }
+    }
+}
+
+impl DerefMut for Life {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.matrix
+    }
+}
+
+impl Life {
+    fn with_starter(starter: &[(usize, usize)]) -> Self {
+        let mut life = Self::default();
+        for (i, j) in starter.iter() {
+            life[*i][*j] = true;
+        }
+        life
+    }
+
+    fn play(self) -> Life {
+        const OFFSETS: &[(i64, i64)] = &[
+            (-1, -1),
+            (-1, 0),
+            (-1, 1),
+            (0, -1),
+            (0, 1),
+            (1, -1),
+            (1, 0),
+            (1, 1),
+        ];
+        let mut future = Life::default();
+        self.iter().enumerate().for_each(|(row, line)| {
+            line.iter().enumerate().for_each(|(column, alive)| {
+                let neighbors = OFFSETS
+                    .iter()
+                    .filter(|offset| {
+                        let neighbor = ((row as i64) + offset.0, (column as i64) + offset.1);
+                        let i = neighbor.0;
+                        let j = neighbor.1;
+                        i >= 0
+                            && j >= 0
+                            && (i as usize) < HEIGHT
+                            && (j as usize) < WIDTH
+                            && self[i as usize][j as usize]
+                    })
+                    .count();
+                future[row][column] =
+                    (*alive && (neighbors == 2 || neighbors == 3)) || (!*alive && neighbors == 3);
+            })
+        });
+        future
+    }
+
+    /// Buffers output into one string per frame since println does not buffer output in uefi-rs.
+    fn render(&self) -> Frame {
+        let mut frame = Frame::default();
+        for l in self.iter() {
+            for cell in l.iter() {
+                if *cell {
+                    frame.push('X').unwrap()
+                } else {
+                    frame.push(' ').unwrap()
+                }
+            }
+            frame.push('\n').unwrap()
+        }
+        frame
+    }
+}
+
+type FrameBuffer = String<{ (WIDTH * HEIGHT) + HEIGHT }>;
+
+#[derive(Default)]
+struct Frame {
+    buffer: FrameBuffer,
+}
+
+impl Deref for Frame {
+    type Target = FrameBuffer;
+
+    fn deref(&self) -> &Self::Target {
+        &self.buffer
+    }
+}
+
+impl DerefMut for Frame {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.buffer
+    }
+}
+
+const BREEDER: &[(usize, usize)] = &[(15, 16), (16, 15), (16, 16), (17, 16), (17, 17)];
+const RENDER_DELAY: usize = 100_000;
+
 #[entry]
 fn main() -> Status {
     uefi::helpers::init().unwrap();
-    info!("Hello world!");
 
-    const W: usize = 30;
-    const H: usize = 30;
+    let mut life = Life::with_starter(&BREEDER);
 
-    let mut life: Vec<Vec<bool, W>, H> = life();
-    for (i, j) in [(15, 16), (16, 15), (16, 16), (17, 16), (17, 17)] {
-        life[i][j] = true;
-    }
+    for _ in repeat(()) {
+        life = life.play();
 
-    for _i in 0..100_000 {
-        life = gol(life.clone());
-        let mut out: String<{ (W * H) + H }> = String::default();
-        for l in life.iter() {
-            for cell in l.iter() {
-                if *cell {
-                    out.push('X').unwrap()
-                } else {
-                    out.push(' ').unwrap()
-                }
-            }
-            out.push('\n').unwrap()
-        }
+        let frame = life.render();
         with_stdout(|stdout| {
             stdout.reset(true).unwrap();
-            stdout.write_str(&out).unwrap()
+            stdout.write_str(&frame).unwrap()
         });
-        boot::stall(100_000);
+        // Stall the loop a bit so it is easier to watch
+        boot::stall(RENDER_DELAY);
     }
 
-    boot::stall(100_000_000);
     Status::SUCCESS
 }
-
-fn gol<const W: usize, const H: usize>(cells: Vec<Vec<bool, W>, H>) -> Vec<Vec<bool, W>, H> {
-    let mut future = life();
-    cells.iter().enumerate().for_each(|(row, line)| {
-        line.iter().enumerate().for_each(|(column, cell)| {
-            let neighbors = [
-                (-1 as i64, -1),
-                (-1, 0),
-                (-1, 1 as i64),
-                (0, -1),
-                (0, 1),
-                (1, -1),
-                (1, 0),
-                (1, 1),
-            ]
-            .iter()
-            .filter_map(|offset| {
-                let neighbor = (
-                    (row as i64).checked_add(offset.0),
-                    (column as i64).checked_add(offset.1),
-                );
-                match neighbor {
-                    (Some(r), Some(w))
-                        if r >= 0
-                            && w >= 0
-                            && (r as usize) < H
-                            && (w as usize) < W
-                            && cells[r as usize][w as usize] =>
-                    {
-                        Some(())
-                    }
-                    _ => None,
-                }
-            })
-            .count();
-            future[row][column] =
-                (*cell && (neighbors == 2 || neighbors == 3)) || (!*cell && neighbors == 3);
-        })
-    });
-    future
-}
-
-fn life<const W: usize, const H: usize>() -> Vec<Vec<bool, W>, H> {
-    let mut future: Vec<Vec<bool, W>, H> = Vec::default();
-    for _ in 0..H {
-        future.push(repeat(false).take(W).collect()).unwrap();
-    }
-    future
-}