diff --git a/demo/main.cpp b/demo/main.cpp
index e2481d8a894d89e4c4881ef26357604daa16876b..c54a40a8383bc6fff2b3d09eaaadcd549868c1d1 100644
--- a/demo/main.cpp
+++ b/demo/main.cpp
@@ -332,12 +332,21 @@ public:
 
     void update() override {
         process_camera(m_world, m_inputs);
+
+        if ( m_inputs->keyboard.pressed(glfwGetKeyScancode(GLFW_KEY_F10)) ) {
+            auto dbgui = fggl::util::ServiceLocator::instance().providePtr<fggl::debug::DebugUI>();
+            dbgui->visible( !dbgui->visible() );
+        }
     }
 
     void render() override {
+
+    }
+
+    // entity inspector
+    void debugInspector(bool* visible) {
         auto types = m_world.types();
 
-        // ECS3 inspector
         ImGui::Begin("Entities");
         auto entityItr = m_world.all();
         for (auto& entity : entityItr) {
@@ -352,112 +361,111 @@ public:
             }
         }
         ImGui::End();
+    };
+
+private:
+    fggl::ecs3::World& m_world;
+    InputManager m_inputs;
+};
 
-        // imgui gamepad debug
-        if ( m_gamepadWindow ) {
-            auto &gamepads = m_inputs->gamepads;
-            ImGui::Begin("GamePad", &m_gamepadWindow);
-            for (int i = 0; i < 16; i++) {
-                std::string title = gamepads.name(i);
-
-                bool present = gamepads.present(i);
-                if (ImGui::TreeNode(title.c_str())) {
-                    ImGui::Text("present: %s", present ? "yes" : "no");
-
-                    if (present) {
-
-                        if (ImGui::TreeNode("buttons##2")) {
-                            for (auto &btn: fggl::input::GamepadButtonsMicrosoft) {
-                                ImGui::Text("%s: %i %i %i", btn.name,
-                                            gamepads.button(i, btn.id),
-                                            gamepads.buttonPressed(i, btn.id),
-                                            gamepads.buttonReleased(i, btn.id)
-                                );
-                            }
-                            ImGui::TreePop();
+void gamepadDebug(bool* visible) {
+    auto inputs = fggl::util::ServiceLocator::instance().providePtr<fggl::input::Input>();
+        auto &gamepads = inputs->gamepads;
+        ImGui::Begin("GamePad", visible);
+        for (int i = 0; i < 16; i++) {
+            std::string title = gamepads.name(i);
+
+            bool present = gamepads.present(i);
+            if (ImGui::TreeNode(title.c_str())) {
+                ImGui::Text("present: %s", present ? "yes" : "no");
+
+                if (present) {
+
+                    if (ImGui::TreeNode("buttons##2")) {
+                        for (auto &btn: fggl::input::GamepadButtonsMicrosoft) {
+                            ImGui::Text("%s: %i %i %i", btn.name,
+                                        gamepads.button(i, btn.id),
+                                        gamepads.buttonPressed(i, btn.id),
+                                        gamepads.buttonReleased(i, btn.id)
+                            );
                         }
+                        ImGui::TreePop();
+                    }
 
-                        if (ImGui::TreeNode("axes##2")) {
-                            for (auto &axis: fggl::input::GamepadAxes) {
-                                ImGui::Text("%s: %f %f", axis.name,
-                                            gamepads.axis(i, axis.id),
-                                            gamepads.axisDelta(i, axis.id)
-                                );
+                    if (ImGui::TreeNode("axes##2")) {
+                        for (auto &axis: fggl::input::GamepadAxes) {
+                            ImGui::Text("%s: %f %f", axis.name,
+                                        gamepads.axis(i, axis.id),
+                                        gamepads.axisDelta(i, axis.id)
+                            );
 
-                            }
-                            ImGui::TreePop();
                         }
-
+                        ImGui::TreePop();
                     }
 
-                    ImGui::TreePop();
-                    ImGui::Separator();
                 }
 
+                ImGui::TreePop();
+                ImGui::Separator();
             }
-            ImGui::End();
-        }
-    }
 
-private:
-    fggl::ecs3::World& m_world;
-    InputManager m_inputs;
-    bool m_gamepadWindow = false;
-};
+        }
+        ImGui::End();
+}
 
 int main(int argc, char* argv[]) {
     auto& locator = fggl::util::ServiceLocator::instance();
 
-    // setup ECS
+    // setup ECS Types
     fggl::ecs3::TypeRegistry types;
     types.make<fggl::math::Transform>();
     fggl::ecs3::ModuleManager modules(types);
 
+    // setup ECS
     fggl::ecs3::World ecs(types);
 
     // input management
     auto inputs = std::make_shared<fggl::input::Input>();
     locator.supply<fggl::input::Input>(inputs);
 
-    auto glfwModule = modules.load<fggl::gfx::ecsGlfwModule>(inputs);
-
     // window
-    fggl::gfx::Window win{};
-    win.title("FGGL Demo");
-	win.fullscreen( true );
+    auto glfwModule = modules.load<fggl::gfx::ecsGlfwModule>(inputs);
+    auto window = glfwModule->createWindow("Demo Game");
+	window->fullscreen( true );
 
 	// storage API
-	fggl::data::Storage storage;
+    auto storage = std::make_shared<fggl::data::Storage>();
+    locator.supply<fggl::data::Storage>(storage);
 	//discover( storage.resolvePath(fggl::data::Data, "../../packs") );
 
 	// Opengl APIs
-    auto glModule = modules.load<fggl::gfx::ecsOpenGLModule>(win, storage);
-
+    auto glModule = modules.load<fggl::gfx::ecsOpenGLModule>(window, storage);
 	fggl::gfx::loadPipeline(glModule, "unlit", false);
 	fggl::gfx::loadPipeline(glModule, "phong", false);
 	fggl::gfx::loadPipeline(glModule, "normals", false);
 
 	// debug layer
-	fggl::debug::DebugUI debug(win);
-	debug.visible(true);
-
-    // Create scene manager
+	std::shared_ptr<fggl::debug::DebugUI> debug = std::make_shared<fggl::debug::DebugUI>(window);
+    locator.supply<fggl::debug::DebugUI>(debug);
+    debug->addWindow("gamepad", gamepadDebug);
+    debug->addWindow("imgui-demo", ImGui::ShowDemoWindow);
+    debug->addWindow("imgui-about", ImGui::ShowAboutWindow);
+    debug->addWindow("imgui-help", [](bool* val) { ImGui::ShowUserGuide(); } );
+	debug->visible(true);
+
+    // Scene management
     auto scenes = std::make_shared<fggl::scenes::SceneManager>();
     locator.supply<fggl::scenes::SceneManager>(scenes);
-
     scenes->create("main_menu", std::make_shared<MenuScene>(inputs));
     scenes->create("game", std::make_shared<GameScene>(ecs, inputs));
-
-    // activate the main menu
     scenes->activate("main_menu");
 
-	// create ECS
-
+    // Main game/event loop
 	fggl::util::Timer time{};
 	time.frequency( glfwGetTimerFrequency() );
 	time.setup( glfwGetTimerValue() );
 
-	while( !win.closeRequested() ) {
+	while( !window->closeRequested() ) {
 		//
 		// Setup setup
 		//
@@ -465,7 +473,7 @@ int main(int argc, char* argv[]) {
 		inputs->frame( time.delta() );
 
 		glfwModule->context.pollEvents();
-		debug.frameStart();
+		debug->frameStart();
 
 		//
 		// update step
@@ -473,15 +481,15 @@ int main(int argc, char* argv[]) {
         scenes->update();
 
         // render the scene
-        win.activate();
+        window->activate();
         glModule->ogl.clear();
 
         // allow the scene to do stuff, then actually render
         scenes->render();
         fggl::gfx::renderMeshes(glModule, ecs, time.delta());
 
-        debug.draw();
-        win.swap();
+        debug->draw();
+        window->swap();
     }
 
 	return 0;
diff --git a/fggl/debug/debug.cpp b/fggl/debug/debug.cpp
index 399fa2410ab879ebff89b24bf21d8af0573812c9..9e825fdb51d01721fd078d31d93328e415b76df1 100644
--- a/fggl/debug/debug.cpp
+++ b/fggl/debug/debug.cpp
@@ -7,14 +7,14 @@
 using fggl::gfx::Window;
 using fggl::debug::DebugUI;
 
-DebugUI::DebugUI(Window& win) : m_visible(false) {
+DebugUI::DebugUI(std::shared_ptr<Window> win) : m_visible(false) {
 	IMGUI_CHECKVERSION();
 	ImGui::CreateContext();
 
     ImGuiIO& io = ImGui::GetIO();
-    io.IniFilename = NULL;
+    io.IniFilename = nullptr;
 
-    ImGui_ImplGlfw_InitForOpenGL(win.handle(), true);
+    ImGui_ImplGlfw_InitForOpenGL(win->handle(), true);
 	ImGui_ImplOpenGL3_Init("#version 130");
 }
 
@@ -32,11 +32,13 @@ void DebugUI::frameStart() {
 }
 
 void DebugUI::draw() {
+    for ( auto& [name, data] : m_windows) {
+        if ( data.m_visible ) {
+            data.m_callback( &data.m_visible );
+        }
+    }
+
 	ImGui::Render();
 	if ( m_visible )
 		ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
 }
-
-void DebugUI::showDemo() {
-	ImGui::ShowDemoWindow();
-}
diff --git a/fggl/debug/debug.h b/fggl/debug/debug.h
index 6b1e162c845544d449454fc1a62e49f8f9a35cb7..cdfb29899cbf4fbe26105febd2b55cf2ce829318 100644
--- a/fggl/debug/debug.h
+++ b/fggl/debug/debug.h
@@ -1,27 +1,47 @@
 #ifndef FGGL_DEBUG_H
 #define FGGL_DEBUG_H
 
+#include <utility>
+#include <functional>
+#include <unordered_map>
+
 #include <fggl/gfx/window.hpp>
 
 namespace fggl::debug {
 
+    using DebugUIDraw = std::function<void(bool*)>;
+
+    struct DebugWindow {
+        bool m_visible;
+        DebugUIDraw m_callback;
+    };
+
 	class DebugUI {
 		public:
-			DebugUI(gfx::Window& window);
+			explicit DebugUI(std::shared_ptr<gfx::Window> window);
 			~DebugUI();
 
 			void frameStart();
 			void draw();
 
-			void showDemo();
+            inline void addWindow(const std::string& name, DebugUIDraw window){
+                m_windows[name] = DebugWindow{
+                    .m_visible = true,
+                    .m_callback = std::move(window)
+                };
+            }
 
 			inline void visible(bool state) {
 				m_visible = state;
 			}
 
+            inline bool visible() const {
+                return m_visible;
+            }
+
 		private:
 			bool m_visible;
-
+            std::unordered_map<std::string, DebugWindow> m_windows;
 	};
 
 }
diff --git a/fggl/gfx/compat.hpp b/fggl/gfx/compat.hpp
index 089ac569e0becc1d9568e6dfca3ee5dbce60baca..b7933526ed94f0a3f02c74853c989beda24a51df 100644
--- a/fggl/gfx/compat.hpp
+++ b/fggl/gfx/compat.hpp
@@ -28,6 +28,13 @@ namespace fggl::gfx {
         ecsGlfwModule(std::shared_ptr<fggl::input::Input> inputs) : context(std::move(inputs) ) {
 		}
 
+        inline
+        std::shared_ptr<Window> createWindow(const std::string& title) {
+            auto window = std::make_shared<Window>();
+            window->title(title);
+            return window;
+        }
+
         [[nodiscard]]
         std::string name() const override {
             return "gfx::glfw";
diff --git a/fggl/gfx/ogl/backend.cpp b/fggl/gfx/ogl/backend.cpp
index 4aa000c669528b8722d81c772e74c08ac32d9326..3a864ade4bab0200eefb2f9a8b660eaaaa0eb4f2 100644
--- a/fggl/gfx/ogl/backend.cpp
+++ b/fggl/gfx/ogl/backend.cpp
@@ -4,9 +4,9 @@
 
 using namespace fggl::gfx;
 
-GlGraphics::GlGraphics(Window& window) : m_window(window) {
+GlGraphics::GlGraphics(const std::shared_ptr<Window> window) : m_window(window) {
     std::cerr << "[OGL] attaching window context" << std::endl;
-	window.activate();
+	window->activate();
 	GLenum err = glewInit();
 	if ( GLEW_OK != err ) {
 		throw std::runtime_error("couldn't init glew");
@@ -21,7 +21,7 @@ GlGraphics::~GlGraphics() {
 }
 
 void GlGraphics::clear() {
-	m_window.activate();
+	m_window->activate();
 
 	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
 	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
diff --git a/fggl/gfx/ogl/backend.hpp b/fggl/gfx/ogl/backend.hpp
index 13075a1b78d1cf84c6ce03400638b1bca5fe4d55..96b863e0d410f1505d0e87249fc0f4f0bf8bf45b 100644
--- a/fggl/gfx/ogl/backend.hpp
+++ b/fggl/gfx/ogl/backend.hpp
@@ -14,13 +14,13 @@ namespace fggl::gfx {
 
 	class GlGraphics {
 		public:
-			GlGraphics(Window& window);
+			GlGraphics(const std::shared_ptr<Window> window);
 			~GlGraphics();
 
 			void clear();
 
 		private:
-			Window& m_window;
+			std::shared_ptr<Window> m_window;
 	};
 
 	class Shader {
diff --git a/fggl/gfx/ogl/compat.hpp b/fggl/gfx/ogl/compat.hpp
index fd761c62bf38044fc31ab4b82c22ca4780694405..a29b7357072a368300861c7ad964d927405e525d 100644
--- a/fggl/gfx/ogl/compat.hpp
+++ b/fggl/gfx/ogl/compat.hpp
@@ -18,6 +18,7 @@
 #include <fggl/gfx/common.hpp>
 #include <fggl/gfx/camera.hpp>
 #include <fggl/ecs/ecs.hpp>
+#include <utility>
 
 namespace fggl::gfx {
 
@@ -29,8 +30,8 @@ namespace fggl::gfx {
 		fggl::gfx::MeshRenderer renderer;
 		fggl::gfx::ShaderCache cache;
 
-		ecsOpenGLModule(Window& window, fggl::data::Storage& storage) :
-			ogl(window), renderer(), cache(storage) {	}
+		ecsOpenGLModule(std::shared_ptr<Window> window, std::shared_ptr<fggl::data::Storage> storage) :
+			ogl(std::move(window)), renderer(), cache(std::move(storage)) {	}
 
         std::string name() const override {
             return "gfx::opengl";
diff --git a/fggl/gfx/ogl/shader.cpp b/fggl/gfx/ogl/shader.cpp
index c3558de127c555dd03b1a231b3cb83d6abcc65cb..d9c0da50d4373e5e724d270d6ef32d2055765606 100644
--- a/fggl/gfx/ogl/shader.cpp
+++ b/fggl/gfx/ogl/shader.cpp
@@ -8,7 +8,7 @@ using namespace fggl::gfx;
 
 bool ShaderCache::compileShader(const std::string& fname, GLuint sid) {
 	std::string source;
-	bool result = m_storage.load(fggl::data::Data, fname, &source);
+	bool result = m_storage->load(fggl::data::Data, fname, &source);
 	if ( !result ) {
         std::cerr << ">> Error loading file: " << fname << std::endl;
 		return false;
@@ -40,7 +40,7 @@ bool ShaderCache::compileShader(const std::string& fname, GLuint sid) {
 	return true;
 }
 
-ShaderCache::ShaderCache(fggl::data::Storage& storage) : m_storage(storage), m_shaders(), m_binary(true) {
+ShaderCache::ShaderCache(std::shared_ptr<fggl::data::Storage> storage) : m_storage(storage), m_shaders(), m_binary(true) {
 
 	if ( !GLEW_ARB_get_program_binary ) {
 		std::cerr << "shader caching disabled, no card support" << std::endl;
@@ -53,7 +53,7 @@ bool ShaderCache::loadFromDisk(GLuint pid, const ShaderConfig& config) {
 
 	BinaryCache cache;
 	auto fname = "shader_" + config.name + ".bin";
-	bool status = m_storage.load( fggl::data::Cache, fname, &cache );
+	bool status = m_storage->load( fggl::data::Cache, fname, &cache );
 
 	if ( !status ) {
 		std::cerr << "loading shader from disk failed" << std::endl;
@@ -71,7 +71,7 @@ void ShaderCache::saveToDisk(GLuint pid, const ShaderConfig& config) {
 	cacheSave(pid, &cache);
 
 	auto fname = "shader_" + config.name + ".bin";
-	m_storage.save( fggl::data::Cache, fname, &cache);
+	m_storage->save( fggl::data::Cache, fname, &cache);
 }
 
 GLuint ShaderCache::getOrLoad(const ShaderConfig& config) {
diff --git a/fggl/gfx/ogl/shader.hpp b/fggl/gfx/ogl/shader.hpp
index 4e06dbd41399b6d79ac4ba1b2e3820f2acd07675..b7e0d48bb1a6b70e25323e3bb95aed323451362f 100644
--- a/fggl/gfx/ogl/shader.hpp
+++ b/fggl/gfx/ogl/shader.hpp
@@ -30,7 +30,7 @@ namespace fggl::gfx {
 
 	class ShaderCache {
 		public:
-			ShaderCache(fggl::data::Storage& storage);
+			ShaderCache(std::shared_ptr<fggl::data::Storage> storage);
 			~ShaderCache() = default;
 
 			GLuint load(const ShaderConfig& config);
@@ -39,7 +39,7 @@ namespace fggl::gfx {
 			GLuint get(const std::string& name);
 
 		private:
-			fggl::data::Storage& m_storage;
+			std::shared_ptr<fggl::data::Storage> m_storage;
 			std::unordered_map<std::string, GLuint> m_shaders;
 
 			// opengl operations