diff --git a/Cargo.toml b/Cargo.toml
index bf862527..7b5fdf22 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,6 +3,7 @@ members = ["./game/game_logic", "./game/desktop_wrapper"]
 exclude = [
     "./third_party/raylib-rs/raylib",
     "./third_party/raylib-rs/raylib-sys",
+    "./automation/anim_stitcher"
 ]
 
 [profile.release]
diff --git a/automation/anim_stitcher/Cargo.toml b/automation/anim_stitcher/Cargo.toml
new file mode 100644
index 00000000..4dfb9c31
--- /dev/null
+++ b/automation/anim_stitcher/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "anim_stitcher"
+publish = false
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+[dependencies]
+serde = { version = "1.0.126", features = ["derive"] }
+serde_json = "1.0.64"
+build-helper = { version = "0.1.1", optional = true }
+image = { version = "0.24.1", optional = true }
+regex = { version = "1.5.5", optional = true }
+
+[features]
+default = []
+generator = ["build-helper", "image", "regex"]
diff --git a/automation/anim_stitcher/src/generator.rs b/automation/anim_stitcher/src/generator.rs
new file mode 100644
index 00000000..d0352931
--- /dev/null
+++ b/automation/anim_stitcher/src/generator.rs
@@ -0,0 +1,66 @@
+use std::path::{Path, PathBuf};
+
+use image::{GenericImage, ImageResult};
+use regex::Regex;
+
+const SPRITE_TYPE_RE: &str = r"^([a-z]+)_";
+
+fn sprite_name_to_output_dir(sprite_name: &str) -> PathBuf {
+    // Determine the sprite type
+    let sprite_type: String = Regex::new(SPRITE_TYPE_RE)
+        .unwrap()
+        .captures(sprite_name)
+        .unwrap()
+        .get(1)
+        .unwrap()
+        .as_str()
+        .to_string()
+        .into();
+
+    // Build the output directory
+    Path::new("../dist/gen/anm/")
+        .join(sprite_type)
+        .join(sprite_name)
+}
+
+pub fn stitch_sprites(sprite_name: &str, ordered_image_files: Vec<PathBuf>) -> ImageResult<()> {
+    build_helper::warning!(
+        "Generating sprite `{}` from {} images",
+        sprite_name,
+        ordered_image_files.len()
+    );
+
+    // Collect all images into a vec of actual image data
+    let mut images = Vec::new();
+    for image_file in ordered_image_files {
+        let image_data = image::open(image_file.as_path()).unwrap();
+        images.push(image_data);
+    }
+
+    // Calculate the final width and height of the sprite
+    let img_width_out: u32 = images.iter().map(|im| im.width()).sum();
+    let img_height_out: u32 = images.iter().map(|im| im.height()).max().unwrap_or(0);
+
+    // Initialize an image buffer with the appropriate size.
+    let mut imgbuf = image::ImageBuffer::new(img_width_out, img_height_out);
+    let mut accumulated_width = 0;
+
+    // Copy each input image at the correct location in the output image.
+    for img in images {
+        imgbuf.copy_from(&img, accumulated_width, 0).unwrap();
+        accumulated_width += img.width();
+    }
+
+    // Get the output directory
+    let output_dir = sprite_name_to_output_dir(sprite_name);
+
+    // Create the output directory if it doesn't exist
+    if !output_dir.exists() {
+        std::fs::create_dir_all(output_dir.as_path()).unwrap();
+    }
+
+    // Write the output image to disk.
+    imgbuf.save(output_dir.join(format!("{}.png", sprite_name)))?;
+
+    Ok(())
+}
diff --git a/automation/anim_stitcher/src/lib.rs b/automation/anim_stitcher/src/lib.rs
new file mode 100644
index 00000000..b852cb30
--- /dev/null
+++ b/automation/anim_stitcher/src/lib.rs
@@ -0,0 +1,3 @@
+
+#[cfg(feature = "generator")]
+pub mod generator;
\ No newline at end of file
diff --git a/game/auto_stitch/README.md b/game/auto_stitch/README.md
new file mode 100644
index 00000000..81903a25
--- /dev/null
+++ b/game/auto_stitch/README.md
@@ -0,0 +1,9 @@
+# The Auto-Stitch directory
+
+This is a bit of a *magic* directory. Anything put in here will automatically be turned into a spritesheet at compile time.
+
+## File organization
+
+In this directory, framesets are expected to be stored in subdirectories. The names of these are important as they will translate into the names of the spritesheets.
+
+For example, if you put a set of frames in `auto_stitch/chr_testFox`, you will get a spritesheet generated to `dist/gen/anm/chr/chr_testFox.png`.
diff --git a/assets/demilurii/test/preview-01.jpg b/game/auto_stitch/chr_testFox/preview-01.jpg
similarity index 100%
rename from assets/demilurii/test/preview-01.jpg
rename to game/auto_stitch/chr_testFox/preview-01.jpg
diff --git a/assets/demilurii/test/preview-02.jpg b/game/auto_stitch/chr_testFox/preview-02.jpg
similarity index 100%
rename from assets/demilurii/test/preview-02.jpg
rename to game/auto_stitch/chr_testFox/preview-02.jpg
diff --git a/assets/demilurii/test/preview-03.jpg b/game/auto_stitch/chr_testFox/preview-03.jpg
similarity index 100%
rename from assets/demilurii/test/preview-03.jpg
rename to game/auto_stitch/chr_testFox/preview-03.jpg
diff --git a/assets/demilurii/test/preview-04.jpg b/game/auto_stitch/chr_testFox/preview-04.jpg
similarity index 100%
rename from assets/demilurii/test/preview-04.jpg
rename to game/auto_stitch/chr_testFox/preview-04.jpg
diff --git a/assets/demilurii/test/preview-05.jpg b/game/auto_stitch/chr_testFox/preview-05.jpg
similarity index 100%
rename from assets/demilurii/test/preview-05.jpg
rename to game/auto_stitch/chr_testFox/preview-05.jpg
diff --git a/assets/demilurii/test/preview-06.jpg b/game/auto_stitch/chr_testFox/preview-06.jpg
similarity index 100%
rename from assets/demilurii/test/preview-06.jpg
rename to game/auto_stitch/chr_testFox/preview-06.jpg
diff --git a/assets/demilurii/test/preview-07.jpg b/game/auto_stitch/chr_testFox/preview-07.jpg
similarity index 100%
rename from assets/demilurii/test/preview-07.jpg
rename to game/auto_stitch/chr_testFox/preview-07.jpg
diff --git a/assets/demilurii/test/preview-08.jpg b/game/auto_stitch/chr_testFox/preview-08.jpg
similarity index 100%
rename from assets/demilurii/test/preview-08.jpg
rename to game/auto_stitch/chr_testFox/preview-08.jpg
diff --git a/assets/demilurii/test/preview-09.jpg b/game/auto_stitch/chr_testFox/preview-09.jpg
similarity index 100%
rename from assets/demilurii/test/preview-09.jpg
rename to game/auto_stitch/chr_testFox/preview-09.jpg
diff --git a/assets/demilurii/test/preview-10.jpg b/game/auto_stitch/chr_testFox/preview-10.jpg
similarity index 100%
rename from assets/demilurii/test/preview-10.jpg
rename to game/auto_stitch/chr_testFox/preview-10.jpg
diff --git a/assets/demilurii/test/preview-11.jpg b/game/auto_stitch/chr_testFox/preview-11.jpg
similarity index 100%
rename from assets/demilurii/test/preview-11.jpg
rename to game/auto_stitch/chr_testFox/preview-11.jpg
diff --git a/assets/demilurii/test/preview-12.jpg b/game/auto_stitch/chr_testFox/preview-12.jpg
similarity index 100%
rename from assets/demilurii/test/preview-12.jpg
rename to game/auto_stitch/chr_testFox/preview-12.jpg
diff --git a/assets/demilurii/test/preview-13.jpg b/game/auto_stitch/chr_testFox/preview-13.jpg
similarity index 100%
rename from assets/demilurii/test/preview-13.jpg
rename to game/auto_stitch/chr_testFox/preview-13.jpg
diff --git a/assets/demilurii/test/preview-14.jpg b/game/auto_stitch/chr_testFox/preview-14.jpg
similarity index 100%
rename from assets/demilurii/test/preview-14.jpg
rename to game/auto_stitch/chr_testFox/preview-14.jpg
diff --git a/assets/demilurii/test/preview-15.jpg b/game/auto_stitch/chr_testFox/preview-15.jpg
similarity index 100%
rename from assets/demilurii/test/preview-15.jpg
rename to game/auto_stitch/chr_testFox/preview-15.jpg
diff --git a/assets/demilurii/test/preview-16.jpg b/game/auto_stitch/chr_testFox/preview-16.jpg
similarity index 100%
rename from assets/demilurii/test/preview-16.jpg
rename to game/auto_stitch/chr_testFox/preview-16.jpg
diff --git a/assets/demilurii/test/preview-17.jpg b/game/auto_stitch/chr_testFox/preview-17.jpg
similarity index 100%
rename from assets/demilurii/test/preview-17.jpg
rename to game/auto_stitch/chr_testFox/preview-17.jpg
diff --git a/assets/demilurii/test/preview-18.jpg b/game/auto_stitch/chr_testFox/preview-18.jpg
similarity index 100%
rename from assets/demilurii/test/preview-18.jpg
rename to game/auto_stitch/chr_testFox/preview-18.jpg
diff --git a/game/dist/.gitignore b/game/dist/.gitignore
new file mode 100644
index 00000000..549fe731
--- /dev/null
+++ b/game/dist/.gitignore
@@ -0,0 +1 @@
+/gen/*
\ No newline at end of file
diff --git a/game/game_logic/Cargo.toml b/game/game_logic/Cargo.toml
index 491b31c5..2a432b32 100644
--- a/game/game_logic/Cargo.toml
+++ b/game/game_logic/Cargo.toml
@@ -3,20 +3,26 @@ name = "game_logic"
 publish = false
 version = "0.1.0"
 edition = "2021"
+build = "build.rs"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 [dependencies]
 raylib = { version = "3.7", path = "../../third_party/raylib-rs/raylib" }
 sad_machine = { version = "1.0", path = "../../third_party/sm" }
-tokio = { version = "1.17.0", featurs = ["fs"] }
+tokio = { version = "1.17.0", features = ["fs"] }
 log = "0.4.14"
 profiling = "1.0.5"
 serde = { version = "1.0.136", features = ["derive"] }
 serde_json = "1.0.79"
 directories = "4.0.1"
-chrono = { verison = "0.4.19", features = ["serde"] }
+chrono = { version = "0.4.19", features = ["serde"] }
 discord-sdk = "0.3.0"
 rust-embed = { version = "6.2.0", features = ["compression"] }
 thiserror = "1.0.30"
 # nalgebra = { version = "0.30.1", features = ["serde"] }
-approx = "0.5.1"
\ No newline at end of file
+approx = "0.5.1"
+
+[build-dependencies.anim_stitcher]
+version = "*"
+features = ["generator"]
+path = "../../automation/anim_stitcher"
diff --git a/game/game_logic/build.rs b/game/game_logic/build.rs
new file mode 100644
index 00000000..67d595c9
--- /dev/null
+++ b/game/game_logic/build.rs
@@ -0,0 +1,26 @@
+//! This script contains code that generates more code.
+//!
+//! The idea being that we can have assets auto-pack and optimize themselves at build time.
+
+fn main() {
+    // We want to re-build if the assets change
+    println!("cargo:rerun-if-changed=../auto_stitch");
+
+    // Search for all direct children of the auto_stitch directory
+    for entry in std::fs::read_dir("../auto_stitch").unwrap() {
+        let entry = entry.unwrap();
+        let path = entry.path();
+
+        // Get all children of the current entry
+        if let Ok(children) = std::fs::read_dir(&path) {
+            let children_paths = children.map(|e| e.unwrap().path()).collect::<Vec<_>>();
+
+            // Process into a sprite
+            anim_stitcher::generator::stitch_sprites(
+                path.file_name().unwrap().to_str().unwrap(),
+                children_paths,
+            )
+            .unwrap();
+        }
+    }
+}