diff --git a/demo/main.cpp b/demo/main.cpp
index f58a6351a3fcf66ac05e68ec6afd9c947cb8f87f..e2481d8a894d89e4c4881ef26357604daa16876b 100644
--- a/demo/main.cpp
+++ b/demo/main.cpp
@@ -1,6 +1,7 @@
 #include <filesystem>
 #include <iostream>
 
+#include <utility>
 #include <fggl/gfx/window.hpp>
 #include <fggl/gfx/camera.hpp>
 
@@ -10,10 +11,12 @@
 #include <fggl/data/procedural.hpp>
 #include <fggl/data/storage.hpp>
 #include <fggl/util/chrono.hpp>
+#include <fggl/util/service.h>
 #include <fggl/ecs3/ecs.hpp>
 
 #include <fggl/debug/debug.h>
 #include <imgui.h>
+#include <fggl/scenes/Scene.h>
 
 constexpr bool showNormals = false;
 
@@ -104,6 +107,7 @@ void process_freecam(fggl::ecs3::World& ecs, InputManager input, fggl::ecs::enti
 	auto code_d = glfwGetKeyScancode(GLFW_KEY_D);
 	auto code_a = glfwGetKeyScancode(GLFW_KEY_A);
 
+
 	// calulate rotation (user input)
 	if ( keyboard.down( code_q ) ) {
 		rotationValue = ROT_SPEED; 
@@ -206,7 +210,7 @@ void process_edgescroll(fggl::ecs3::World& ecs, InputManager input, fggl::ecs::e
 }
 
 
-void process_camera(fggl::gfx::Window& window, fggl::ecs3::World& ecs, InputManager input) {
+void process_camera(fggl::ecs3::World& ecs, InputManager input) {
     auto cameras = ecs.findMatching<fggl::gfx::Camera>();
     fggl::ecs3::entity_t cam = cameras[0];
 
@@ -232,126 +236,114 @@ void process_camera(fggl::gfx::Window& window, fggl::ecs3::World& ecs, InputMana
 	process_edgescroll( ecs, input, cam );
 }
 
-int main(int argc, char* argv[]) {
-	// setup ECS
-    fggl::ecs3::TypeRegistry types;
-    fggl::ecs3::ModuleManager modules(types);
 
-    fggl::ecs3::World ecs(types);
+class MenuScene : public fggl::scenes::Scene {
 
-    auto inputs = std::make_shared<fggl::input::Input>();
-    auto glfwModule = modules.load<fggl::gfx::ecsGlfwModule>(inputs);
+public:
+    explicit MenuScene(InputManager inputs) : m_inputs(std::move(inputs)) {}
+    ~MenuScene() override = default;
 
-	fggl::gfx::Window win{};
-	win.title("FGGL Demo");
-	win.fullscreen( true );
+    void setup() override {
+    }
 
-	// storage API
-	fggl::data::Storage storage;
-	//discover( storage.resolvePath(fggl::data::Data, "../../packs") );
+    void cleanup() override {
 
-	// Opengl APIs
-    auto glModule = modules.load<fggl::gfx::ecsOpenGLModule>(win, storage);
+    }
 
-	fggl::gfx::loadPipeline(glModule, "unlit", false);
-	fggl::gfx::loadPipeline(glModule, "phong", false);
-	fggl::gfx::loadPipeline(glModule, "normals", false);
+    void update() override {
+        bool leftMouse = m_inputs->mouse.button(fggl::input::MouseButton::LEFT);
+        if (leftMouse) {
+            auto scenes = fggl::util::ServiceLocator::instance().providePtr<fggl::scenes::SceneManager>();
+            scenes->activate("game");
+        }
+    }
 
-	// debug layer
-	fggl::debug::DebugUI debug(win);
-	debug.visible(true);
+    void render() override {
 
-	// create ECS
-    types.make<fggl::math::Transform>();
+    }
 
-    // create camera using strings
-    {
-        auto prototype = ecs.create(false);
-        ecs.add(prototype, types.find(fggl::math::Transform::name));
-        ecs.add(prototype, types.find(fggl::gfx::Camera::name));
+    private:
+        InputManager m_inputs;
 
-        auto camTf = ecs.get<fggl::math::Transform>(prototype);
-        camTf->origin( glm::vec3(0.0f, 3.0f, 3.0f) );
-    }
+};
 
-    // create building prototype
-    fggl::ecs3::entity_t bunker;
-    {
-        bunker = ecs.create(true);
-        ecs.add(bunker, types.find(fggl::math::Transform::name));
-
-        // mesh
-        int nSections = 2;
-        constexpr float HALF_PI = M_PI / 2.0f;
-        constexpr char shader[] = "phong";
-
-        fggl::data::Mesh mesh;
-        for (int j=-(nSections/2); j<=nSections/2; j++) {
-            const auto shapeOffset = glm::vec3( 0.0f, 0.0f, j * 1.0f );
-
-            const auto cubeMat = glm::translate( fggl::math::mat4( 1.0f ) , shapeOffset );
-            const auto leftSlope = fggl::math::modelMatrix(
-                    glm::vec3(-1.0f, 0.0f, 0.0f) + shapeOffset,
-                    glm::vec3( 0.0f, -HALF_PI, 0.0f) );
-            const auto rightSlope = fggl::math::modelMatrix(
-                    glm::vec3( 1.0f, 0.0f, 0.0f) + shapeOffset,
-                    glm::vec3( 0.0f, HALF_PI, 0.0f) );
-
-            fggl::data::make_cube( mesh, cubeMat );
-            fggl::data::make_slope( mesh, leftSlope );
-            fggl::data::make_slope( mesh, rightSlope );
-        }
-        mesh.removeDups();
+class GameScene : public fggl::scenes::Scene {
+public:
+    explicit GameScene(fggl::ecs3::World& world, InputManager inputs) : m_world(world), m_inputs(std::move(inputs)) { };
+    ~GameScene() override = default;
 
-        fggl::gfx::StaticMesh staticMesh{mesh, shader};
-        ecs.set<fggl::gfx::StaticMesh>(bunker, &staticMesh);
-    }
+    void setup() override {
+        auto types = m_world.types();
 
-	auto floorEnt = ecs.create(false);
-	{
-		ecs.add<fggl::math::Transform>( floorEnt );
-		fggl::data::Mesh mesh = fggl::data::make_quad_xz();
+        // create camera using strings
+        {
+            auto prototype = m_world.create(false);
+            m_world.add(prototype, types.find(fggl::math::Transform::name));
+            m_world.add(prototype, types.find(fggl::gfx::Camera::name));
 
-        fggl::gfx::StaticMesh sMesh{mesh, "phong"};
-		ecs.set<fggl::gfx::StaticMesh>(floorEnt, &sMesh);
-	}
+            auto camTf = m_world.get<fggl::math::Transform>(prototype);
+            camTf->origin( glm::vec3(0.0f, 3.0f, 3.0f) );
+        }
 
-    int nCubes = 3;
-	for ( int i=0; i<nCubes; i++ ) {
-        auto bunkerClone = ecs.copy(bunker);
-        auto result = ecs.get<fggl::math::Transform>(bunkerClone);
-		result->origin( glm::vec3( i * 5.0f, 0.0f, 0.0f) );
-	}
+        // create building prototype
+        fggl::ecs3::entity_t bunker;
+        {
+            bunker = m_world.create(true);
+            m_world.add(bunker, types.find(fggl::math::Transform::name));
+
+            // mesh
+            int nSections = 2;
+            constexpr float HALF_PI = M_PI / 2.0f;
+            constexpr char shader[] = "phong";
+
+            fggl::data::Mesh mesh;
+            for (int j=-(nSections/2); j<=nSections/2; j++) {
+                const auto shapeOffset = glm::vec3( 0.0f, 0.0f, j * 1.0f );
+
+                const auto cubeMat = glm::translate( fggl::math::mat4( 1.0f ) , shapeOffset );
+                const auto leftSlope = fggl::math::modelMatrix(
+                        glm::vec3(-1.0f, 0.0f, 0.0f) + shapeOffset,
+                        glm::vec3( 0.0f, -HALF_PI, 0.0f) );
+                const auto rightSlope = fggl::math::modelMatrix(
+                        glm::vec3( 1.0f, 0.0f, 0.0f) + shapeOffset,
+                        glm::vec3( 0.0f, HALF_PI, 0.0f) );
+
+                fggl::data::make_cube( mesh, cubeMat );
+                fggl::data::make_slope( mesh, leftSlope );
+                fggl::data::make_slope( mesh, rightSlope );
+            }
+            mesh.removeDups();
 
-	bool gamepadWindow = true;
+            fggl::gfx::StaticMesh staticMesh{mesh, shader};
+            m_world.set<fggl::gfx::StaticMesh>(bunker, &staticMesh);
+        }
 
-	fggl::util::Timer time{};
-	time.frequency( glfwGetTimerFrequency() );
-	time.setup( glfwGetTimerValue() );
+        int nCubes = 3;
+        for ( int i=0; i<nCubes; i++ ) {
+            auto bunkerClone = m_world.copy(bunker);
+            auto result = m_world.get<fggl::math::Transform>(bunkerClone);
+            result->origin( glm::vec3( i * 5.0f, 0.0f, 0.0f) );
+        }
+    }
 
-	while( !win.closeRequested() ) {
+    void cleanup() override {
 
-		//
-		// Setup setup
-		//
-		time.tick( glfwGetTimerValue() );
-		inputs->frame( time.delta() );
+    }
 
-		glfwModule->context.pollEvents();
-		debug.frameStart();
+    void update() override {
+        process_camera(m_world, m_inputs);
+    }
 
-		//
-		// update step
-		// 
-		process_camera(win, ecs, inputs);
+    void render() override {
+        auto types = m_world.types();
 
         // ECS3 inspector
         ImGui::Begin("Entities");
-        auto entityItr = ecs.all();
+        auto entityItr = m_world.all();
         for (auto& entity : entityItr) {
             std::string label = "entity-" + std::to_string(entity);
             if ( ImGui::TreeNode(label.c_str()) ){
-                auto entComp = ecs.getComponents(entity);
+                auto entComp = m_world.getComponents(entity);
                 for ( auto comp : entComp ){
                     auto meta = types.meta(comp);
                     ImGui::Text("%s (%d) - %lu bytes", meta->name(), comp, meta->size());
@@ -361,10 +353,10 @@ int main(int argc, char* argv[]) {
         }
         ImGui::End();
 
-		// imgui gamepad debug
-        if ( gamepadWindow ) {
-            auto &gamepads = inputs->gamepads;
-            ImGui::Begin("GamePad", &gamepadWindow);
+        // 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);
 
@@ -405,20 +397,92 @@ int main(int argc, char* argv[]) {
             }
             ImGui::End();
         }
+    }
+
+private:
+    fggl::ecs3::World& m_world;
+    InputManager m_inputs;
+    bool m_gamepadWindow = false;
+};
 
+int main(int argc, char* argv[]) {
+    auto& locator = fggl::util::ServiceLocator::instance();
+
+    // setup ECS
+    fggl::ecs3::TypeRegistry types;
+    types.make<fggl::math::Transform>();
+    fggl::ecs3::ModuleManager modules(types);
+
+    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 );
+
+	// storage API
+	fggl::data::Storage storage;
+	//discover( storage.resolvePath(fggl::data::Data, "../../packs") );
+
+	// Opengl APIs
+    auto glModule = modules.load<fggl::gfx::ecsOpenGLModule>(win, 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
+    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
+
+	fggl::util::Timer time{};
+	time.frequency( glfwGetTimerFrequency() );
+	time.setup( glfwGetTimerValue() );
+
+	while( !win.closeRequested() ) {
+		//
+		// Setup setup
 		//
-		// render step
-		// 
-		win.activate();
-		glModule->ogl.clear();
+		time.tick( glfwGetTimerValue() );
+		inputs->frame( time.delta() );
 
-		// model rendering system
-		fggl::gfx::renderMeshes(glModule, ecs, time.delta());
-		debug.draw();
+		glfwModule->context.pollEvents();
+		debug.frameStart();
 
-		// swap the windows - frame rendering over
-		win.swap();
-	}
+		//
+		// update step
+		//
+        scenes->update();
+
+        // render the scene
+        win.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();
+    }
 
 	return 0;
 }
diff --git a/fggl/CMakeLists.txt b/fggl/CMakeLists.txt
index 1bd8aec5da984df76d65488a9b019a21d15734be..155d5bd41d5aee4e6a85447ab64a543f9485c462 100644
--- a/fggl/CMakeLists.txt
+++ b/fggl/CMakeLists.txt
@@ -6,7 +6,7 @@ add_library(fggl fggl.cpp
 	data/procedural.cpp
 	ecs3/fast/Container.cpp
 	ecs3/prototype/world.cpp
-	ecs3/module/module.cpp)
+	ecs3/module/module.cpp scenes/Scene.cpp scenes/Scene.h util/service.h)
 target_include_directories(fggl PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../)
 
 # Graphics backend
diff --git a/fggl/ecs3/prototype/world.h b/fggl/ecs3/prototype/world.h
index 74b59b2463ef0e53912f4c25b5d7062b05b06946..5111928fb674e4b1642a1ca30c996be18ce19ff7 100644
--- a/fggl/ecs3/prototype/world.h
+++ b/fggl/ecs3/prototype/world.h
@@ -122,6 +122,10 @@ namespace fggl::ecs3::prototype {
                 m_entities.erase(entity);
             }
 
+            inline TypeRegistry& types() {
+                return m_types;
+            }
+
             std::vector<entity_t> all() {
                 std::vector<entity_t> entities{};
                 for( auto& [eid, entity] : m_entities) {
diff --git a/fggl/scenes/Scene.cpp b/fggl/scenes/Scene.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b3d2dab5b94d442991a820ebde09f4c45b34d96e
--- /dev/null
+++ b/fggl/scenes/Scene.cpp
@@ -0,0 +1,25 @@
+//
+// Created by webpigeon on 20/11/2021.
+//
+
+#include "Scene.h"
+
+#include <utility>
+
+namespace fggl::scenes {
+
+    void SceneManager::create(const std::string &name, std::shared_ptr<Scene> scene) {
+        m_scenes[name] = std::move(scene);
+    }
+
+    void SceneManager::activate(const std::string &name) {
+        auto newScene = m_scenes.at(name);
+        if ( m_active != nullptr ) {
+            m_active->cleanup();
+            m_active = nullptr;
+        }
+        newScene->setup();
+        m_active = newScene;
+    }
+
+}
\ No newline at end of file
diff --git a/fggl/scenes/Scene.h b/fggl/scenes/Scene.h
new file mode 100644
index 0000000000000000000000000000000000000000..d4c9daf1aa98d6ed6ad40e2788a5ed8ef0cea01a
--- /dev/null
+++ b/fggl/scenes/Scene.h
@@ -0,0 +1,49 @@
+//
+// Created by webpigeon on 20/11/2021.
+//
+
+#ifndef FGGL_SCENE_H
+#define FGGL_SCENE_H
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+namespace fggl::scenes {
+
+    class Scene {
+        public:
+            virtual ~Scene() = default;
+
+            virtual void setup() = 0;
+            virtual void cleanup() = 0;
+
+            virtual void update() = 0;
+            virtual void render() = 0;
+    };
+
+    class SceneManager {
+        public:
+            SceneManager() = default;
+
+            void create(const std::string& name, std::shared_ptr<Scene> scene);
+            void activate(const std::string& name);
+
+            inline void update() {
+                if ( m_active == nullptr ) { return; }
+                m_active->update();
+            }
+
+            inline void render() {
+                if ( m_active == nullptr ) { return; }
+                m_active->render();
+            }
+
+        private:
+            std::shared_ptr<Scene> m_active;
+            std::unordered_map<std::string, std::shared_ptr<Scene>> m_scenes;
+    };
+
+}
+
+#endif //FGGL_SCENE_H
diff --git a/fggl/util/service.h b/fggl/util/service.h
new file mode 100644
index 0000000000000000000000000000000000000000..9d1c26108c224409a204bbbed41d2462d02629ae
--- /dev/null
+++ b/fggl/util/service.h
@@ -0,0 +1,47 @@
+//
+// Created by webpigeon on 20/11/2021.
+//
+
+#ifndef FGGL_SERVICE_H
+#define FGGL_SERVICE_H
+
+#include <memory>
+#include <typeindex>
+#include <unordered_map>
+
+namespace fggl::util {
+
+    class ServiceLocator {
+        inline static ServiceLocator *s_instance;
+        std::unordered_map<std::type_index, std::shared_ptr<void>> m_services;
+        ServiceLocator() = default;
+
+    public:
+        // don't allow copying or moving
+        ServiceLocator(ServiceLocator& other) = delete;
+        ServiceLocator(ServiceLocator&& other) = delete;
+
+        static ServiceLocator& instance() {
+            if ( s_instance == nullptr ) {
+                s_instance = new ServiceLocator();
+            }
+            return *s_instance;
+        }
+
+        template<typename T>
+        void supply(std::shared_ptr<T> ptr) {
+            auto info = std::type_index(typeid(T));
+            m_services[info] = ptr;
+        }
+
+        template<typename T>
+        std::shared_ptr<T> providePtr() {
+            auto info = std::type_index(typeid(T));
+            return std::static_pointer_cast<T>(m_services.at(info));
+        }
+
+    };
+
+}
+
+#endif //FGGL_SERVICE_H