diff --git a/demo/main.cpp b/demo/main.cpp
index 07cb316a8d693122d1903cd0676ac795d802b9c6..0fc4475731a419efc5c4305dc7ab709917bc1378 100644
--- a/demo/main.cpp
+++ b/demo/main.cpp
@@ -5,10 +5,9 @@
 #include <iostream>
 
 #include <fggl/gfx/window.hpp>
-#include <fggl/gfx/ogl.hpp>
-#include <fggl/gfx/renderer.hpp>
-#include <fggl/gfx/shader.hpp>
 #include <fggl/gfx/camera.hpp>
+#include <fggl/gfx/ogl/compat.hpp>
+
 #include <fggl/data/procedural.hpp>
 #include <fggl/ecs/ecs.hpp>
 #include <fggl/debug/debug.h>
@@ -178,45 +177,24 @@ int main(int argc, char* argv[]) {
 	win.title("FGGL Demo");
 	win.fullscreen( true );
 
-	// opengl time
-	fggl::gfx::Graphics ogl(win);
-	fggl::gfx::MeshRenderer meshRenderer;
-
-	// debug layer
-	fggl::debug::DebugUI debug(win);
-	debug.visible(true);
+	// setup ECS
+	fggl::ecs::ECS ecs;
 
 	// storage API
 	fggl::data::Storage storage;
 	discover( storage.resolvePath(fggl::data::Data, "res") );
 
-	fggl::gfx::ShaderCache cache(storage);
-
-	fggl::gfx::ShaderConfig config;
-	config.name = "unlit";
-	config.vertex = "unlit_vert.glsl";
-	config.fragment = "unlit_frag.glsl";
-	auto shader = cache.load(config);
-
-	fggl::gfx::ShaderConfig configPhong;
-	configPhong.name = "phong";
-	configPhong.vertex = configPhong.name + "_vert.glsl";
-	configPhong.fragment = configPhong.name + "_frag.glsl";
-//	configPhong.fragment = configPhong.name + "_normals_frag.glsl";
-	auto shaderPhong = cache.load(configPhong);
-
-	fggl::gfx::ShaderConfig configNormals;
-	configNormals.name = "normals";
-	configNormals.hasGeom = true;
-	configNormals.vertex = configNormals.name + "_vert.glsl";
-	configNormals.geometry = configNormals.name + "_geom.glsl";
-	configNormals.fragment = configNormals.name + "_frag.glsl";
-	auto shaderNormals = cache.load( configNormals );
+	// Opengl APIs
+	auto glModule = fggl::gfx::ecsInitOpenGL(ecs, 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 ECS
-	fggl::ecs::ECS ecs;
-	ecs.registerComponent<fggl::gfx::MeshToken>();
-	ecs.registerComponent<fggl::gfx::Camera>();
 	ecs.registerComponent<fggl::math::Transform>();
 
 	// make camera
@@ -231,11 +209,10 @@ int main(int argc, char* argv[]) {
 	auto floorEnt = ecs.createEntity();
 	{
 		ecs.addComponent<fggl::math::Transform>( floorEnt );
-
 		fggl::data::Mesh mesh = fggl::data::make_quad_xz();
 
-		auto token = meshRenderer.upload( mesh );
-		auto bounds = ecs.addComponent<fggl::gfx::MeshToken>(floorEnt, token);
+		ecs.addComponent<fggl::gfx::StaticMesh>(floorEnt, mesh, "phong");
+		fggl::gfx::onStaticMeshAdded(ecs, floorEnt, glModule);
 	}
 
 	int nCubes = 3;
@@ -266,11 +243,11 @@ int main(int argc, char* argv[]) {
 			fggl::data::make_slope( mesh, leftSlope );
 			fggl::data::make_slope( mesh, rightSlope );
 		}
-
 		mesh.removeDups();
-		auto token = meshRenderer.upload( mesh );
-		token.pipeline = shaderPhong;
-		ecs.addComponent<fggl::gfx::MeshToken>(entity, token);
+		ecs.addComponent<fggl::gfx::StaticMesh>(entity, mesh, "phong");
+
+		// pretend we have callbacks
+		fggl::gfx::onStaticMeshAdded(ecs, entity, glModule);
 	}
 
 	fggl::gfx::Input& input = fggl::gfx::Input::instance();
@@ -404,26 +381,14 @@ int main(int argc, char* argv[]) {
 		}*/
 
 		// render step
-		ogl.clear();
-
-		// render using real shader
-		auto renderables = ecs.getEntityWith<fggl::gfx::MeshToken>();
-		for ( auto renderable : renderables ) {
-			auto token = ecs.getComponent<fggl::gfx::MeshToken>(renderable);
-			token->pipeline = shaderPhong;
-		}
-		meshRenderer.render(win, ecs, camEnt, 16.0f);
-
-		// render using normals shader
-		if ( showNormals ) {
-			for ( auto renderable : renderables ) {
-				auto token = ecs.getComponent<fggl::gfx::MeshToken>(renderable);
-				token->pipeline = shaderNormals;
-			}
-			meshRenderer.render(win, ecs, camEnt, 16.0f);
-		}
+		win.activate();
+		glModule->ogl.clear();
 
+		// model rendering system
+		fggl::gfx::renderMeshes(glModule, ecs, dt);
 		debug.draw();
+
+		// swap the windows - frame rendering over
 		win.swap();
 	}
 
diff --git a/fggl/gfx/CMakeLists.txt b/fggl/gfx/CMakeLists.txt
index 961857887e88901d4ec059d044e0f45ce0aaa1d2..0e81d51759fe6378d716ecc34227bf8d50db6fc7 100644
--- a/fggl/gfx/CMakeLists.txt
+++ b/fggl/gfx/CMakeLists.txt
@@ -3,19 +3,9 @@
 target_sources(fggl
     PRIVATE
 	window.cpp
-	renderer.cpp
 	input.cpp
-	shader.cpp
-	ogl.cpp
 )
 
-# OpenGL Backend
-find_package( OpenGL REQUIRED )
-include_directories( ${OPENGL_INCLUDE_DIR} )
-target_link_libraries(fggl OpenGL::OpenGL GLEW)
+# OpenGL backend
+add_subdirectory(ogl)
 
-# GLEW
-find_package( GLEW REQUIRED )
-include_directories( ${GLEW_INCLUDE_DIRS} )
-
-find_package( glm REQUIRED )
diff --git a/fggl/gfx/common.hpp b/fggl/gfx/common.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1192659b66c748eb1cbcc42f431d14e91f4fd28d
--- /dev/null
+++ b/fggl/gfx/common.hpp
@@ -0,0 +1,25 @@
+#ifndef FGGL_GFX_COMMON_H
+#define FGGL_GFX_COMMON_H
+
+// load the correct rendering backend (only opengl for now)
+#include <fggl/gfx/ogl/common.hpp>
+
+// now it's safe to load the windowing system
+#include <GLFW/glfw3.h>
+
+#include <fggl/data/model.hpp>
+#include <string>
+
+namespace fggl::gfx {
+
+	struct StaticMesh {
+		data::Mesh mesh;
+		std::string pipeline;
+
+		inline StaticMesh(const data::Mesh& aMesh, const std::string& aPipeline) :
+			mesh(aMesh), pipeline(aPipeline) {}
+	};
+
+}
+
+#endif
diff --git a/fggl/gfx/ogl/CMakeLists.txt b/fggl/gfx/ogl/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d746aebf88144248bb17bc9be0335717d56ebb84
--- /dev/null
+++ b/fggl/gfx/ogl/CMakeLists.txt
@@ -0,0 +1,19 @@
+
+# Sources
+target_sources(fggl
+    PRIVATE
+	backend.cpp
+	shader.cpp
+	renderer.cpp
+)
+
+# OpenGL Backend
+find_package( OpenGL REQUIRED )
+include_directories( ${OPENGL_INCLUDE_DIR} )
+target_link_libraries(fggl OpenGL::OpenGL GLEW)
+
+# GLEW
+find_package( GLEW REQUIRED )
+include_directories( ${GLEW_INCLUDE_DIRS} )
+
+find_package( glm REQUIRED )
diff --git a/fggl/gfx/ogl.cpp b/fggl/gfx/ogl/backend.cpp
similarity index 92%
rename from fggl/gfx/ogl.cpp
rename to fggl/gfx/ogl/backend.cpp
index 706420976ec6127f46b15038e4d6138111d26345..3585d91b3424dde33bf9ad9aa5bd1f85a1e50b17 100644
--- a/fggl/gfx/ogl.cpp
+++ b/fggl/gfx/ogl/backend.cpp
@@ -1,4 +1,4 @@
-#include <fggl/gfx/ogl.hpp>
+#include <fggl/gfx/ogl/backend.hpp>
 
 #include <stdexcept>
 
diff --git a/fggl/gfx/ogl.hpp b/fggl/gfx/ogl/backend.hpp
similarity index 92%
rename from fggl/gfx/ogl.hpp
rename to fggl/gfx/ogl/backend.hpp
index 0cc71387046c28988a34759825d3d140ffd49cf5..13075a1b78d1cf84c6ce03400638b1bca5fe4d55 100644
--- a/fggl/gfx/ogl.hpp
+++ b/fggl/gfx/ogl/backend.hpp
@@ -1,8 +1,8 @@
-#ifndef FGGL_GFX_OGL_H
-#define FGGL_GFX_OGL_H
+#ifndef FGGL_GFX_OGL_BACKEND_H
+#define FGGL_GFX_OGL_BACKEND_H
 
-#include "window.hpp"
-#include "rendering.hpp"
+#include <fggl/gfx/ogl/common.hpp>
+#include <fggl/gfx/window.hpp>
 
 #include <stdexcept>
 
diff --git a/fggl/gfx/rendering.cpp b/fggl/gfx/ogl/common.hpp
similarity index 52%
rename from fggl/gfx/rendering.cpp
rename to fggl/gfx/ogl/common.hpp
index 4577c57c8a6573d564f2a16af3e693143fc6b148..28786f54456740ddc12c6bdb53fb97b39e4b5417 100644
--- a/fggl/gfx/rendering.cpp
+++ b/fggl/gfx/ogl/common.hpp
@@ -1,11 +1,10 @@
-#ifndef FGGL_GFX_RENDERING_H
-#define FGGL_GFX_RENDERING_H
+#ifndef FGGL_GFX_OGL_COMMON_H
+#define FGGL_GFX_OGL_COMMON_H
 
 /**
  * Ensure Graphics libraries are in the right order.
  */
 
 #include <GL/glew.h>
-#include <GLFW/glfw3.h>
 
 #endif
diff --git a/fggl/gfx/ogl/compat.hpp b/fggl/gfx/ogl/compat.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0cc905df047fd66ef6d4649a465bd1b8c36f2447
--- /dev/null
+++ b/fggl/gfx/ogl/compat.hpp
@@ -0,0 +1,98 @@
+#ifndef FGGL_GFX_OGL_COMPAT_H
+#define FGGL_GFX_OGL_COMPAT_H
+/**
+ * Legacy/Direct OpenGL calls.
+ *
+ * This shouldn't be exposed to the demo app, but the ECS we're using isn't smart enouph to allow us to
+ * abstract this yet. It's next thing on the list, but this branch is about cleaning up OpenGL not about
+ * extending our ECS.
+ *
+ * Should be removed when the engine has suitable abstractions in place.
+ */
+
+#include <fggl/gfx/ogl/shader.hpp>
+#include <fggl/gfx/ogl/renderer.hpp>
+
+#include <fggl/gfx/common.hpp>
+#include <fggl/gfx/camera.hpp>
+#include <fggl/ecs/ecs.hpp>
+
+namespace fggl::gfx {
+
+	//
+	// fake module support - allows us to still RAII
+	//
+	struct ecsOpenGLModule {
+		fggl::gfx::Graphics ogl;
+		fggl::gfx::MeshRenderer renderer;
+		fggl::gfx::ShaderCache cache;
+
+		inline ecsOpenGLModule(Window& window, fggl::data::Storage& storage) : 
+			ogl(window), renderer(), cache(storage) {
+		}
+	};
+	using OglModule = std::shared_ptr<ecsOpenGLModule>;
+
+	//
+	// Loading related functions - should be handled in modules/data-driven
+	//
+	inline void loadPipeline(OglModule& mod, const std::string& name, bool hasGeom) {
+		fggl::gfx::ShaderConfig config;
+		config.name = name;
+		config.vertex = name+"_vert.glsl";
+		config.fragment = name+"_frag.glsl";
+		if ( hasGeom ) {
+			config.geometry = name+"_geom.glsl";
+		}
+		mod->cache.load(config);
+	}
+
+	inline void loadBasicPipeline(OglModule& mod, const std::string &name) {
+		loadPipeline(mod, name, false);
+	}
+
+	//
+	// fake module/callbacks - our ECS doesn't have module/callback support yet.
+	// 
+	inline void onStaticMeshAdded(ecs::ECS& ecs, ecs::entity_t entity, OglModule& mod) {
+		auto meshData = ecs.getComponent<gfx::StaticMesh>(entity);
+		auto pipeline = mod->cache.get(meshData->pipeline);
+
+		auto glMesh = mod->renderer.upload(meshData->mesh);
+		glMesh.pipeline = pipeline;
+
+		ecs.addComponent<fggl::gfx::GlRenderToken>(entity, glMesh);
+	}
+
+	inline void renderMeshes(OglModule& mod, ecs::ECS& ecs, float dt) {
+		// get the camera
+		auto cameras = ecs.getEntityWith<fggl::gfx::Camera>();
+		if ( cameras.empty() ) {
+			return;
+		}
+		auto camera = cameras[0];
+
+		// get the models
+		auto renderables = ecs.getEntityWith<fggl::gfx::GlRenderToken>();
+		for ( auto renderable : renderables ) {
+			mod->renderer.render(ecs, camera, dt);
+		}
+	}
+
+	inline void ecsInitGraphics(ecs::ECS& ecs) {
+		ecs.registerComponent<fggl::gfx::StaticMesh>();
+		ecs.registerComponent<fggl::gfx::Camera>();
+	}
+
+	inline std::shared_ptr<ecsOpenGLModule> ecsInitOpenGL(ecs::ECS& ecs, Window& window, data::Storage& storage) {
+		ecsInitGraphics(ecs);
+
+		auto mod = std::make_shared<ecsOpenGLModule>(window, storage);
+		ecs.registerComponent<fggl::gfx::GlRenderToken>();
+		return mod;
+	}
+
+}
+
+
+#endif
diff --git a/fggl/gfx/renderer.cpp b/fggl/gfx/ogl/renderer.cpp
similarity index 93%
rename from fggl/gfx/renderer.cpp
rename to fggl/gfx/ogl/renderer.cpp
index a3febd7582606128fcb4d077c2e50b577d59f786..5c70dfd565b8608f3cafa65ad294526b24e7b669 100644
--- a/fggl/gfx/renderer.cpp
+++ b/fggl/gfx/ogl/renderer.cpp
@@ -1,9 +1,8 @@
-#include <fggl/gfx/renderer.hpp>
-#include <fggl/gfx/rendering.hpp>
-#include <fggl/gfx/camera.hpp>
 
+#include <fggl/gfx/ogl/renderer.hpp>
 #include <fggl/data/model.hpp>
 
+#include <fggl/gfx/camera.hpp>
 #include <glm/ext/matrix_transform.hpp>
 #include <glm/glm.hpp>
 #include <glm/gtc/type_ptr.hpp>
@@ -67,7 +66,7 @@ GlRenderToken MeshRenderer::upload(fggl::data::Mesh& mesh) {
 	return token;
 }
 
-void MeshRenderer::render(const Window& window, const fggl::ecs::ECS& ecs, const ecs::entity_t camera, float dt) {
+void MeshRenderer::render(const fggl::ecs::ECS& ecs, const ecs::entity_t camera, float dt) {
     if ( camera == ecs::NULL_ENTITY ) return;
 
     auto entities = ecs.getEntityWith<GlRenderToken>();
@@ -75,9 +74,6 @@ void MeshRenderer::render(const Window& window, const fggl::ecs::ECS& ecs, const
 
     total += dt;
 
-    // make sure the correct rendering context is active
-    window.activate();
-
     glEnable(GL_CULL_FACE);
     glCullFace(GL_BACK);
 
diff --git a/fggl/gfx/renderer.hpp b/fggl/gfx/ogl/renderer.hpp
similarity index 76%
rename from fggl/gfx/renderer.hpp
rename to fggl/gfx/ogl/renderer.hpp
index 1772e2f4e6ad4d8a00c121399896be08ab9c29e1..cac965a67f4e041caf9b7ce28d84748a14d94bcf 100644
--- a/fggl/gfx/renderer.hpp
+++ b/fggl/gfx/ogl/renderer.hpp
@@ -5,7 +5,7 @@
 
 #include <fggl/data/model.hpp>
 #include <fggl/ecs/ecs.hpp>
-#include <fggl/gfx/ogl.hpp>
+#include <fggl/gfx/ogl/backend.hpp>
 
 namespace fggl::gfx {
 
@@ -23,8 +23,7 @@ namespace fggl::gfx {
 
 			token_t upload(fggl::data::Mesh& mesh);
 
-			// are VAO safe across opengl contexts? :S
-			void render(const Window& window, const ecs::ECS& ecs, const ecs::entity_t camera, float dt);
+			void render(const ecs::ECS& ecs, const ecs::entity_t camera, float dt);
 
 			float total;
 	};
diff --git a/fggl/gfx/shader.cpp b/fggl/gfx/ogl/shader.cpp
similarity index 99%
rename from fggl/gfx/shader.cpp
rename to fggl/gfx/ogl/shader.cpp
index 144bfa7cefa884cb9bf3da669822545e7c04a9a1..59a10e57f19d1182fb4e616b834f0c0defa5de3a 100644
--- a/fggl/gfx/shader.cpp
+++ b/fggl/gfx/ogl/shader.cpp
@@ -1,5 +1,5 @@
 
-#include <fggl/gfx/shader.hpp>
+#include <fggl/gfx/ogl/shader.hpp>
 
 #include <iostream>
 #include <vector>
diff --git a/fggl/gfx/shader.hpp b/fggl/gfx/ogl/shader.hpp
similarity index 97%
rename from fggl/gfx/shader.hpp
rename to fggl/gfx/ogl/shader.hpp
index e66f9399fdee09e03648e43f9279d5c691d97a7b..4e06dbd41399b6d79ac4ba1b2e3820f2acd07675 100644
--- a/fggl/gfx/shader.hpp
+++ b/fggl/gfx/ogl/shader.hpp
@@ -2,7 +2,7 @@
 #define FGGL_GFX_SHADER_H
 
 #include <cstdio>
-#include <fggl/gfx/ogl.hpp>
+#include <fggl/gfx/ogl/backend.hpp>
 #include <fggl/data/storage.hpp>
 
 #include <filesystem>
diff --git a/fggl/gfx/rendering.hpp b/fggl/gfx/rendering.hpp
deleted file mode 100644
index 4577c57c8a6573d564f2a16af3e693143fc6b148..0000000000000000000000000000000000000000
--- a/fggl/gfx/rendering.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef FGGL_GFX_RENDERING_H
-#define FGGL_GFX_RENDERING_H
-
-/**
- * Ensure Graphics libraries are in the right order.
- */
-
-#include <GL/glew.h>
-#include <GLFW/glfw3.h>
-
-#endif
diff --git a/fggl/gfx/window.hpp b/fggl/gfx/window.hpp
index eaff91042567c14716556f0769a34d487af0382b..eaa80386f58b30273ec0d024f7e67c4efe802b4a 100644
--- a/fggl/gfx/window.hpp
+++ b/fggl/gfx/window.hpp
@@ -4,8 +4,8 @@
 #include <cassert>
 #include <string>
 
+#include <fggl/gfx/common.hpp>
 #include <fggl/math/types.hpp>
-#include <fggl/gfx/rendering.hpp>
 #include <fggl/gfx/input.hpp>
 
 namespace fggl::gfx {