diff --git a/meson.build b/meson.build
index c03cdee..54c00da 100644
--- a/meson.build
+++ b/meson.build
@@ -104,6 +104,7 @@ sources = [
   'pathing.cpp',
   'rand.cpp',
   'raycaster.cpp',
+  'ritual_ui.cpp',
   'rituals.cpp',
   'save.cpp',
   'shiterator.hpp',
diff --git a/ritual_ui.cpp b/ritual_ui.cpp
new file mode 100644
index 0000000..81dcdb1
--- /dev/null
+++ b/ritual_ui.cpp
@@ -0,0 +1,121 @@
+#include "ritual_ui.hpp"
+#include "components.hpp"
+#include "guecs.hpp"
+#include "rand.hpp"
+
+namespace gui {
+  using namespace guecs;
+  using std::any, std::any_cast, std::string, std::make_any;
+
+  RitualUI::RitualUI(GameLevel level) :
+    $level(level)
+  {
+    $gui.position(STATUS_UI_X, STATUS_UI_Y, STATUS_UI_WIDTH, STATUS_UI_HEIGHT);
+    $gui.layout(
+        "[ ritual_ui ]"
+        "[inv_slot1 | inv_slot2 | inv_slot3]"
+        "[inv_slot4 | inv_slot5 | inv_slot6]"
+        "[*%(100,300)circle_area]"
+        "[_]"
+        "[_]");
+  }
+
+  void RitualUI::init() {
+    $gui.world().set_the<Background>({$gui.$parser});
+
+    for(auto& [name, cell] : $gui.cells()) {
+      if(name == "circle_area") {
+        dbc::log("circle area not setup...");
+      } else {
+        auto button = $gui.entity(name);
+        $gui.set<Rectangle>(button, {});
+        $gui.set<Textual>(button, {""});
+        $gui.set<ActionData>(button, {make_any<string>(name)});
+
+        if(name == "ritual_ui") {
+          $gui.set<Clickable>(button, {
+              [this](auto, auto){ select_ritual(); }
+          });
+        }
+      }
+    }
+
+    $ritual_ui = textures::get("ritual_crafting_area");
+    $ritual_ui.sprite->setPosition({0,0});
+    $ritual_ui.sprite->setTextureRect($ritual_closed_rect);
+    $ritual_state = RitualUIState::CLOSED;
+    $ritual_anim.simple = false;
+    $ritual_anim.looped = false;
+    $ritual_anim.easing = ease::NONE;
+    $ritual_anim.stationary = true;
+    $ritual_anim.texture_width = 380;
+    $ritual_anim.frames = 3;
+    $ritual_anim.speed = 0.2f;
+
+    $gui.init();
+  }
+
+  void RitualUI::select_ritual() {
+    using enum RitualUIState;
+
+    switch($ritual_state) {
+      case OPEN:
+        $ritual_state = CLOSING;
+        break;
+      case CLOSED:
+        $ritual_state = OPENING;
+        $ritual_anim.play();
+        break;
+      case OPENING: // ignored
+        break;
+      case CLOSING: // ignored
+        break;
+      default:
+        dbc::sentinel("INVALID RitualUIState");
+    }
+  }
+
+  /* WARNING: This is really not the greatest way to do this. */
+  void RitualUI::update() {
+    dbc::log("RITUAL UPDATE NOT IMPLEMENTED");
+  }
+
+  void RitualUI::render(sf::RenderWindow &window) {
+    sf::IntRect rect;
+    sf::Vector2f scale{1.0, 1.0};
+    sf::Vector2f pos{0, 0};
+
+    using enum RitualUIState;
+    switch($ritual_state) {
+      case OPEN: {
+          rect = $ritual_open_rect;
+        } break;
+      case CLOSED: {
+          rect = $ritual_closed_rect;
+        }
+        break;
+      case OPENING: {
+          if($ritual_anim.playing) {
+            rect = $ritual_closed_rect;
+            $ritual_anim.step(scale, pos, rect);
+          } else {
+            $ritual_state = OPEN;
+            rect = $ritual_open_rect;
+          }
+        }
+        break;
+      case CLOSING: {
+          rect = $ritual_closed_rect;
+          $ritual_state = CLOSED;
+        } break;
+      default:
+        dbc::sentinel("INVALID RitualUIState");
+    }
+
+    $ritual_ui.sprite->setTextureRect(rect);
+    $ritual_ui.sprite->setPosition(pos);
+    $ritual_ui.sprite->setScale(scale);
+
+    window.draw(*$ritual_ui.sprite);
+  }
+}
diff --git a/ritual_ui.hpp b/ritual_ui.hpp
new file mode 100644
index 0000000..d3230b0
--- /dev/null
+++ b/ritual_ui.hpp
@@ -0,0 +1,32 @@
+#pragma once
+#include "levelmanager.hpp"
+#include "constants.hpp"
+#include <deque>
+#include "textures.hpp"
+#include "guecs.hpp"
+
+namespace gui {
+  enum class RitualUIState {
+    OPEN=0,
+    CLOSED=1,
+    OPENING=2,
+    CLOSING=3
+  };
+
+  class RitualUI {
+    public:
+      sf::IntRect $ritual_closed_rect{{0,0},{380,720}};
+      sf::IntRect $ritual_open_rect{{380 * 2,0},{380,720}};
+      RitualUIState $ritual_state = RitualUIState::CLOSED;
+      textures::SpriteTexture $ritual_ui;
+      components::Animation $ritual_anim;
+      guecs::UI $gui;
+      GameLevel $level;
+
+      RitualUI(GameLevel level);
+      void select_ritual();
+      void init();
+      void render(sf::RenderWindow &window);
+      void update();
+  };
+}
diff --git a/status_ui.cpp b/status_ui.cpp
index e385079..ca24455 100644
--- a/status_ui.cpp
+++ b/status_ui.cpp
@@ -58,36 +58,11 @@ namespace gui {
     $ritual_ui = textures::get("ritual_crafting_area");
     $ritual_ui.sprite->setPosition({0,0});
     $ritual_ui.sprite->setTextureRect($ritual_closed_rect);
-    $ritual_state = RitualUIState::CLOSED;
-    $ritual_anim.simple = false;
-    $ritual_anim.looped = false;
-    $ritual_anim.easing = ease::NONE;
-    $ritual_anim.stationary = true;
-    $ritual_anim.texture_width = 380;
-    $ritual_anim.frames = 3;
-    $ritual_anim.speed = 0.2f;
-
     $gui.init();
   }
 
   void StatusUI::select_ritual() {
-    using enum RitualUIState;
-
-    switch($ritual_state) {
-      case OPEN:
-        $ritual_state = CLOSING;
-        break;
-      case CLOSED:
-        $ritual_state = OPENING;
-        $ritual_anim.play();
-        break;
-      case OPENING: // ignored
-        break;
-      case CLOSING: // ignored
-        break;
-      default:
-        dbc::sentinel("INVALID RitualUIState");
-    }
+    dbc::log("ritual selected but no way to trigger it yet");
   }
 
   void StatusUI::select_slot(DinkyECS::Entity ent, any slot_name) {
@@ -152,44 +127,7 @@ namespace gui {
   }
 
   void StatusUI::render(sf::RenderWindow &window) {
-    sf::IntRect rect;
-    sf::Vector2f scale{1.0, 1.0};
-    sf::Vector2f pos{0, 0};
-
-    using enum RitualUIState;
-    switch($ritual_state) {
-      case OPEN: {
-          rect = $ritual_open_rect;
-        } break;
-      case CLOSED: {
-          $gui.render(window);
-          rect = $ritual_closed_rect;
-        }
-        break;
-      case OPENING: {
-          $gui.render(window);
-          if($ritual_anim.playing) {
-            rect = $ritual_closed_rect;
-            $ritual_anim.step(scale, pos, rect);
-          } else {
-            $ritual_state = OPEN;
-            rect = $ritual_open_rect;
-          }
-        }
-        break;
-      case CLOSING: {
-          $gui.render(window);
-          rect = $ritual_closed_rect;
-          $ritual_state = CLOSED;
-        } break;
-      default:
-        dbc::sentinel("INVALID RitualUIState");
-    }
-
-    $ritual_ui.sprite->setTextureRect(rect);
-    $ritual_ui.sprite->setPosition(pos);
-    $ritual_ui.sprite->setScale(scale);
-
+    $gui.render(window);
     window.draw(*$ritual_ui.sprite);
   }
 
diff --git a/status_ui.hpp b/status_ui.hpp
index ba243e3..32c48ff 100644
--- a/status_ui.hpp
+++ b/status_ui.hpp
@@ -6,18 +6,9 @@
 #include "guecs.hpp"
 
 namespace gui {
-  enum class RitualUIState {
-    OPEN=0,
-    CLOSED=1,
-    OPENING=2,
-    CLOSING=3
-  };
-
   class StatusUI {
     public:
       sf::IntRect $ritual_closed_rect{{0,0},{380,720}};
-      sf::IntRect $ritual_open_rect{{380 * 2,0},{380,720}};
-      RitualUIState $ritual_state = RitualUIState::CLOSED;
       textures::SpriteTexture $ritual_ui;
       components::Animation $ritual_anim;
       guecs::UI $gui;