diff --git a/demo/data/phong_frag.glsl b/demo/data/phong_frag.glsl
index 0d4df5e53b15dd65e1d30162932df4a81e8b5ee2..52157698312f71b36869863c746d76292d1ec377 100644
--- a/demo/data/phong_frag.glsl
+++ b/demo/data/phong_frag.glsl
@@ -1,8 +1,17 @@
 #version 330 core
 // based on http://www.opengl-tutorial.org, WTFPL
+// features lighting additions from https://learnopengl.com/, CC BY-NC 4.0
 
+struct Material {
+    vec3 ambient;
+    vec3 diffuse;
+    vec3 specular;
+    float shininess;
+};
+uniform Material material;
 
 uniform vec3 lightPos;
+uniform vec3 objColour;
 
 in vec3 normal;
 
@@ -16,14 +25,8 @@ out vec4 FragColor;
 void main()
 {
     vec3 lightColour = vec3( 1.0, 1.0, 1.0 );
-    vec3 objColour = vec3(1.0f, 0.6f, 0.2f);
     float lightPower = 200.0;
 
-    // material colours
-    vec3 matDiff = vec3(1.0, 0.6, 0.2 );
-    vec3 matAmb = vec3(0.1) * matDiff;
-    vec3 matSpec = vec3(0.3);
-
     vec3 n = normalize( normal_cs );
     vec3 l = normalize( lightdir_cs );
     float distance = length( lightPos - pos_ws );
@@ -31,12 +34,12 @@ void main()
     vec3 e = normalize( eyedir_cs );
     vec3 r = reflect( -l, n );
     float cosAlpha = clamp( dot(e, r), 0, 1);
-
     float cosTheta = clamp( dot( n, l ), 0, 1 );
-    vec3 colour = 
-       ( matAmb ) +
-       ( matDiff * lightColour * lightPower * cosTheta / ( distance*distance ) ) + 
-       ( matSpec * lightColour * lightPower * pow( cosAlpha, 5 ) / (distance*distance) );
+
+    vec3 colour =
+       ( material.ambient * vec3(0.1) ) +
+       ( material.diffuse * vec3(0.5) * lightColour * lightPower * cosTheta / ( distance*distance ) ) +
+       ( material.specular * lightColour * lightPower * pow( cosAlpha, material.shininess * 128 ) / (distance*distance) );
 
     FragColor = vec4(colour, 1);
 } 
diff --git a/demo/demo/rollball.cpp b/demo/demo/rollball.cpp
index c4fb40076d98a850fd9e4399989dd80755e64bb0..d20e2f3e784ad7ffeb309b5a05895f350b14dbb3 100644
--- a/demo/demo/rollball.cpp
+++ b/demo/demo/rollball.cpp
@@ -20,6 +20,8 @@
 
 #include "rollball.hpp"
 #include "fggl/gfx/camera.hpp"
+#include "fggl/gfx/phong.hpp"
+
 #include "fggl/input/camera_input.h"
 #include "fggl/util/service.h"
 #include "fggl/ecs3/prototype/loader.hpp"
@@ -40,11 +42,22 @@ static void setup_prefabs(fggl::ecs3::World& world, Prefabs& prefabs) {
 		// player (cube because my sphere function doesn't exist yet
 		prefabs.player = world.findPrototype("player");
 		world.add<fggl::phys::Dynamics>(prefabs.player);
+
+		auto* material = world.add< fggl::gfx::PhongMaterial >( prefabs.player );
+		material->ambient = fggl::math::vec3(0.25F, 0.25F, 0.25F);
+		material->diffuse = fggl::math::vec3(0.4F, 0.4F, 0.4F);
+		material->specular = fggl::math::vec3(0.774597F, 0.774597F, 0.774597F);
+		material->shininess = 0.6F;
 	}
 
 	{
 		// collectable
 		prefabs.collectable = world.findPrototype("collectable");
+		auto* material = world.add< fggl::gfx::PhongMaterial >( prefabs.collectable );
+		material->ambient = fggl::math::vec3(0.0215F, 0.1754F, 0.0215F);
+		material->diffuse = fggl::math::vec3(0.007568F, 0.61424F, 0.07568F);
+		material->specular = fggl::math::vec3(0.633F, 0.727811F, 0.633F);
+		material->shininess = 0.6F;
 
 		// we need both of these for callbacks to trigger.
 		world.add<fggl::phys::CollisionCallbacks>(prefabs.collectable);
@@ -204,6 +217,10 @@ namespace demo {
 				}
 
 				// rotation
+				float closestDistance = FLT_MAX;
+				fggl::ecs::entity_t closestEntity = fggl::ecs::NULL_ENTITY;
+				auto playerPos = world.get<fggl::math::Transform>(state.player)->origin();
+
 				for ( auto& entity : state.collectables ) {
 					if ( world.alive(entity) ) {
 						auto* transform = world.get<fggl::math::Transform>(entity);
@@ -211,6 +228,15 @@ namespace demo {
 						// rotate the cubes
 						fggl::math::vec3 angles{1.0F, 2.0F, 3.0F};
 						transform->rotateEuler( angles * (60.0F / 1000) );
+
+						auto distance = glm::distance(transform->origin(), playerPos);
+						if ( distance < closestDistance) {
+							closestDistance = distance;
+							closestEntity = entity;
+						}
+
+						//auto* renderer = world.get<fggl::data::StaticMesh>( entity );
+						//renderer->hintColour = fggl::math::vec3(1.0F, 1.0F, 1.0F);
 					}
 				}
 				state.time += (60.0f / 1000);
diff --git a/fggl/gfx/ogl4/models.cpp b/fggl/gfx/ogl4/models.cpp
index f77f3332e73ce26cce66a039c27e2efd8125cc43..db1facd20f19325af4cfdb11779403aece7e7677 100644
--- a/fggl/gfx/ogl4/models.cpp
+++ b/fggl/gfx/ogl4/models.cpp
@@ -26,6 +26,8 @@
 #include "fggl/data/heightmap.h"
 
 #include "fggl/gfx/camera.hpp"
+#include "fggl/gfx/phong.hpp"
+
 #include <spdlog/spdlog.h>
 
 namespace fggl::gfx::ogl4 {
@@ -161,6 +163,19 @@ namespace fggl::gfx::ogl4 {
 				shader->setUniformMtx(shader->uniform("view"), viewMatrix);
 				shader->setUniformMtx(shader->uniform("projection"), projectionMatrix);
 
+				auto* material = world.tryGet<fggl::gfx::PhongMaterial>(entity);
+				if ( material == nullptr) {
+					shader->setUniformF(shader->uniform("material.ambient"), math::vec3(0.05F, 0.05F, 0.05F));
+					shader->setUniformF(shader->uniform("material.diffuse"), math::vec3(0.5F, 0.5F, 0.5F));
+					shader->setUniformF(shader->uniform("material.specular"), math::vec3(0.7F, 0.7F, 0.7F));
+					shader->setUniformF(shader->uniform("material.shininess"), .078125F);
+				} else {
+					shader->setUniformF(shader->uniform("material.ambient"), material->ambient);
+					shader->setUniformF(shader->uniform("material.diffuse"), material->diffuse);
+					shader->setUniformF(shader->uniform("material.specular"), material->specular);
+					shader->setUniformF(shader->uniform("material.shininess"), material->shininess);
+				}
+
 				auto lightPosIdx = shader->uniform("lightPos");
 				if ( lightPosIdx != -1 ) {
 					shader->setUniformF(lightPosIdx, lightPos);
diff --git a/include/fggl/gfx/ogl/compat.hpp b/include/fggl/gfx/ogl/compat.hpp
index d00abfc778d613da2eb3a2c613e23681bf20e03e..fc50420ee643f21d92695c292479e1e287ca4e0d 100644
--- a/include/fggl/gfx/ogl/compat.hpp
+++ b/include/fggl/gfx/ogl/compat.hpp
@@ -22,6 +22,8 @@
 #include <fggl/input/camera_input.h>
 #include <fggl/data/heightmap.h>
 
+#include "fggl/gfx/phong.hpp"
+
 namespace fggl::gfx {
 
 	//
@@ -42,6 +44,8 @@ namespace fggl::gfx {
 			types.make<data::StaticMesh>();
 			types.make<data::HeightMap>();
 
+			types.make<gfx::PhongMaterial>();
+
 			// camera dependencies
 			types.make<fggl::gfx::Camera>();
 			types.make<fggl::input::FreeCamKeys>();
diff --git a/include/fggl/gfx/ogl/types.hpp b/include/fggl/gfx/ogl/types.hpp
index 4be7b8f07c5b2eff423f7281d42a39259bc587bc..9fec627d3fae55c9d39332a6a61281a19c4900fa 100644
--- a/include/fggl/gfx/ogl/types.hpp
+++ b/include/fggl/gfx/ogl/types.hpp
@@ -66,7 +66,11 @@ namespace fggl::gfx::ogl {
 			}
 
 			inline Location uniform(const std::string_view& name) const {
-				return glGetUniformLocation( m_obj, name.data() );
+				auto location = glGetUniformLocation( m_obj, name.data() );
+				if ( location == -1 ) {
+					std::cerr << "error: " << name << " does not exist" << std::endl;
+				}
+				return location;
 			}
 
 			// primatives
@@ -82,15 +86,15 @@ namespace fggl::gfx::ogl {
 			}
 
 			// vector versions (float)
-			inline void setUniformF(Location name, math::vec2f value) {
+			inline void setUniformF(Location name, const math::vec2f& value) {
 				glProgramUniform2f(m_obj, name, value.x, value.y);
 			}
 
-			inline void setUniformF(Location name, math::vec3f value) {
+			inline void setUniformF(Location name, const math::vec3f& value) {
 				glProgramUniform3f(m_obj, name, value.x, value.y, value.z);
 			}
 
-			inline void setUniformF(Location name, math::vec4f value) {
+			inline void setUniformF(Location name, const math::vec4f& value) {
 				glProgramUniform4f(m_obj, name, value.x, value.y, value.z, value.w);
 			}
 
diff --git a/include/fggl/gfx/phong.hpp b/include/fggl/gfx/phong.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2e59714fab9e8f223a2a2de267c690b7afedefbf
--- /dev/null
+++ b/include/fggl/gfx/phong.hpp
@@ -0,0 +1,42 @@
+/*
+ * ${license.title}
+ * Copyright (C) 2022 ${license.owner}
+ * ${license.mailto}
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+//
+// Created by webpigeon on 05/06/22.
+//
+
+#ifndef FGGL_GFX_PHONG_HPP
+#define FGGL_GFX_PHONG_HPP
+
+#include "fggl/math/types.hpp"
+
+namespace fggl::gfx {
+
+	struct PhongMaterial {
+		constexpr static const char* name = "gfx::material";
+		math::vec3 ambient;
+		math::vec3 diffuse;
+		math::vec3 specular;
+		float shininess;
+	};
+
+} // namesapce fggl::gfx
+
+#endif //FGGL_GFX_PHONG_HPP