diff --git a/demo/demo/rollball.cpp b/demo/demo/rollball.cpp
index 4b7c8b70dcb0caca4e63b676f0d89308371251a0..97f56cf8828b5b22546d403a1d2f04e9be074b86 100644
--- a/demo/demo/rollball.cpp
+++ b/demo/demo/rollball.cpp
@@ -114,7 +114,14 @@ static void setupPrefabs(fggl::ecs3::World& world, Prefabs& prefabs) {
 		fggl::data::make_cube(meshComponent.mesh);
 		world.set<fggl::data::StaticMesh>(prefabs.collectable, &meshComponent);
 
-		auto* callbacks = world.add<fggl::phys::CollisionCallbacks>(prefabs.collectable);
+		// physics stuff
+		auto* rb = world.add<fggl::phys::RigidBody>(prefabs.collectable);
+		rb->shape = new fggl::phys::Box({0.5f, 0.5f, 0.5f});
+		rb->type = fggl::phys::BodyType::KINEMATIC;
+
+		// we need both of these for callbacks to trigger.
+		world.add<fggl::phys::CollisionCallbacks>(prefabs.collectable);
+		world.add<fggl::phys::CollisionCache>(prefabs.collectable);
 	}
 
 }
@@ -200,12 +207,21 @@ namespace demo {
 		Prefabs prefabs{};
 		setupPrefabs(world(), prefabs);
 
+		// collectable callbacks
+		auto* collectableCallbacks = world().get<fggl::phys::CollisionCallbacks>(prefabs.collectable);
+		collectableCallbacks->onEnter = [this](auto ourID, auto theirID) {
+			if ( theirID == player) {
+				this->world().destroy(ourID);
+			}
+		};
+
 		// actual scene objects
 		setupCamera(world());
 
 		// create a 20x20 grid
 		fggl::math::vec2 size{40.0f, 40.0f};
 		player = setupEnvironment(world(), prefabs, size);
+
 	}
 
 	void RollBall::update() {
diff --git a/fggl/phys/bullet/simulation.cpp b/fggl/phys/bullet/simulation.cpp
index 5524cf55640c8afcd4b61d6d269d5079d5cf875e..da1291ae1b00b71e788cb899ab24684ad45aa38f 100644
--- a/fggl/phys/bullet/simulation.cpp
+++ b/fggl/phys/bullet/simulation.cpp
@@ -37,6 +37,8 @@ namespace fggl::phys::bullet {
 		m_debug = std::make_unique<debug::BulletDebugDrawList>();
 		m_world->setDebugDrawer( m_debug.get() );
 		m_debug->setDebugMode(1);
+
+		m_ecs->addDeathListener( [this](auto entity) { this->onEntityDeath(entity);} );
 	}
 
 	void BulletPhysicsEngine::step() {
@@ -66,6 +68,7 @@ namespace fggl::phys::bullet {
 		m_world->debugDrawWorld();
 	}
 
+
 	static void build_motation_state(math::Transform* myState, BulletBody* btState) {
 		auto myPos = myState->origin();
 		btVector3 position{myPos.x, myPos.y, myPos.z};
@@ -203,12 +206,12 @@ namespace fggl::phys::bullet {
 			auto itr = cache->collisions.find(other);
 			if ( itr == cache->collisions.end() ) {
 				if ( callbacks->onEnter != nullptr ) {
-					callbacks->onEnter(other);
+					callbacks->onEnter(owner, other);
 				}
-				cache->collisions.insert( other );
+				cache->collisions.insert(  other );
 			} else {
 				if ( callbacks->onStay != nullptr ) {
-					callbacks->onStay(other);
+					callbacks->onStay(owner, other);
 				}
 			}
 		}
@@ -231,13 +234,23 @@ namespace fggl::phys::bullet {
 			const auto* body0 = contactManifold->getBody0();
 			const auto* body1 = contactManifold->getBody1();
 
-			handleCollisionCallbacks(m_ecs, body0->getUserIndex(), body1->getUserIndex());
-			handleCollisionCallbacks(m_ecs, body1->getUserIndex(), body0->getUserIndex());
+			// FIXME rigid body didn't die according to plan!
+			if ( body0->getUserIndex() == ecs::NULL_ENTITY || body1->getUserIndex() == ecs::NULL_ENTITY) {
+				continue;
+			}
+
+			if ( !m_ecs->alive( body0->getUserIndex()) || !m_ecs->alive(body1->getUserIndex()) ) {
+				continue;
+			}
 
 			std::cerr << "contact: " << body0->getUserIndex() << " on " << body1->getUserIndex() << std::endl;
+
+			handleCollisionCallbacks(m_ecs, body0->getUserIndex(), body1->getUserIndex());
+			handleCollisionCallbacks(m_ecs, body1->getUserIndex(), body0->getUserIndex());
 		}
 
 		// note conacts that have ended
+		caches = m_ecs->findMatching<CollisionCache>(); // re-fetch, entities can (and probably do) die in handlers.
 		for( auto& ent : caches) {
 			auto* cache = m_ecs->get<CollisionCache>(ent);
 
@@ -251,10 +264,18 @@ namespace fggl::phys::bullet {
 			for (const auto& other : cache->lastFrame) {
 				auto itr = cache->collisions.find(other);
 				if (itr == cache->collisions.end()) {
-					callbacks->onExit(other);
+					callbacks->onExit( ent,other);
 				}
 			}
 		}
 	}
 
+	void BulletPhysicsEngine::onEntityDeath(ecs::entity_t entity)  {
+			auto* btPhysics = m_ecs->tryGet<BulletBody>(entity);
+			if ( btPhysics != nullptr) {
+				btPhysics->body->setUserIndex( ecs::NULL_ENTITY );
+				m_world->removeRigidBody( btPhysics->body );
+			}
+	}
+
 } // namespace fggl::phys::bullet
\ No newline at end of file
diff --git a/fggl/scenes/game.cpp b/fggl/scenes/game.cpp
index 92aace65b711366bc0ec61b08e911ed1dc725cca..b2f3ad583a62e6d4d95cee1720429a308d4a4cd1 100644
--- a/fggl/scenes/game.cpp
+++ b/fggl/scenes/game.cpp
@@ -59,6 +59,8 @@ namespace fggl::scenes {
 		if ( m_phys != nullptr ) {
 			m_phys->step();
 		}
+
+		m_world->reapEntities();
 	}
 
 	void Game::render(fggl::gfx::Graphics &gfx) {
diff --git a/include/fggl/ecs3/prototype/world.h b/include/fggl/ecs3/prototype/world.h
index 4cc8a5bdbb790b7c82972446685cfe34c3f693a7..5a178f5d7f28e2a7222130e36a680f592b82a280 100644
--- a/include/fggl/ecs3/prototype/world.h
+++ b/include/fggl/ecs3/prototype/world.h
@@ -6,6 +6,9 @@
 #define FGGL_ECS3_PROTOTYPE_WORLD_H
 
 #include <map>
+#include <functional>
+#include <unordered_set>
+
 #include <fggl/ecs3/types.hpp>
 
 /**
@@ -15,6 +18,8 @@
  */
 namespace fggl::ecs3::prototype {
 
+	using entity_cb = std::function<void(const entity_t)>;
+
 	class Entity {
 		public:
 			bool m_abstract;
@@ -122,9 +127,39 @@ namespace fggl::ecs3::prototype {
 				return clone;
 			}
 
+			bool alive(entity_t entity) const {
+				if (entity == NULL_ENTITY) {
+					// tis a silly question, but can be asked by accident.
+					return false;
+				}
+
+				if ( m_killList.find( entity ) != m_killList.end() ) {
+					// getting reaped
+					return false;
+				}
+
+				// check liveness
+				return m_entities.find( entity ) != m_entities.end();
+			}
+
 			void destroy(entity_t entity) {
+				assert( entity != NULL_ENTITY && "attempted to kill null entity" );
 				// TOOD resolve and clean components
-				m_entities.erase(entity);
+				//m_entities.erase(entity);
+				m_killList.insert(entity);
+			}
+
+			void reapEntities() {
+				for (const auto& entity : m_killList) {
+					//auto& entityObj = m_entities.at(entity);
+					//entityObj.clear();
+					for (auto& listener : m_deathListeners) {
+						listener( entity );
+					}
+
+					m_entities.erase(entity);
+				}
+				m_killList.clear();
 			}
 
 			inline TypeRegistry &types() {
@@ -140,6 +175,8 @@ namespace fggl::ecs3::prototype {
 			}
 
 			std::vector<component_type_t> getComponents(entity_t entityID) {
+				assert(alive(entityID) && "attempted to get components on dead entity");
+
 				std::vector<component_type_t> components{};
 				auto &entity = m_entities.at(entityID);
 				auto comps = entity.getComponentIDs();
@@ -168,6 +205,8 @@ namespace fggl::ecs3::prototype {
 
 			template<typename C>
 			C *add(entity_t entity_id) {
+				assert( entity_id != NULL_ENTITY && "attempted to add component on null entity" );
+
 				//spdlog::info("component '{}' added to '{}'", C::name, entity_id);
 				auto &entity = m_entities.at(entity_id);
 				auto comp = entity.template add<C>();
@@ -177,6 +216,8 @@ namespace fggl::ecs3::prototype {
 			}
 
 			void *add(entity_t entity_id, component_type_t component_id) {
+				assert( entity_id != NULL_ENTITY && "attempted to add component on null entity" );
+
 				auto meta = m_types.meta(component_id);
 				auto &entity = m_entities.at(entity_id);
 
@@ -187,6 +228,8 @@ namespace fggl::ecs3::prototype {
 
 			template<typename C>
 			C *set(entity_t entity_id, const C *ptr) {
+				assert( entity_id != NULL_ENTITY && "attempted to set component on null entity" );
+
 				//spdlog::info("component '{}' set on '{}'", C::name, entity_id);
 				auto &entity = m_entities.at(entity_id);
 				auto comp = entity.set<C>(ptr);
@@ -196,6 +239,8 @@ namespace fggl::ecs3::prototype {
 			}
 
 			void *set(entity_t entity_id, component_type_t cid, const void *ptr) {
+				assert( entity_id != NULL_ENTITY && "attempted to set component on null entity" );
+
 				auto &entity = m_entities.at(entity_id);
 				auto cMeta = m_types.meta(cid);
 
@@ -206,6 +251,8 @@ namespace fggl::ecs3::prototype {
 
 			template<typename C>
 			C* tryGet(entity_t entity_id) const {
+				assert( entity_id != NULL_ENTITY && "attempted to tryGet component on null entity" );
+
 				try {
 					const auto &entity = m_entities.at(entity_id);
 					try {
@@ -214,13 +261,15 @@ namespace fggl::ecs3::prototype {
 						return nullptr;
 					}
 				} catch ( std::out_of_range& e) {
-					std::cerr << "someone requested an component that didn't exist, entity was: " << entity_id << std::endl;
+					std::cerr << "someone requested an entity that didn't exist, entity was: " << entity_id << std::endl;
 					return nullptr;
 				}
 			}
 
 			template<typename C>
 			C *get(entity_t entity_id) const {
+				assert( entity_id != NULL_ENTITY && "attempted to get component on null entity" );
+
 				C* ptr = tryGet<C>(entity_id);
 				if (ptr == nullptr) {
 					std::cerr << "entity " << entity_id << " does not have component "<< C::name << std::endl;
@@ -230,6 +279,8 @@ namespace fggl::ecs3::prototype {
 
 			template<typename C>
 			void remove(entity_t entity_id) {
+				assert( entity_id != NULL_ENTITY && "attempted to remove component on null entity" );
+
 				try {
 					auto &entity = m_entities.at(entity_id);
 					try {
@@ -247,10 +298,16 @@ namespace fggl::ecs3::prototype {
 				return entity.get(t);
 			}
 
+			void addDeathListener(const entity_cb& callback) {
+				m_deathListeners.emplace_back(callback);
+			}
+
 		private:
+			std::vector< entity_cb > m_deathListeners;
 			TypeRegistry &m_types;
 			entity_t m_next;
 			std::map<entity_t, Entity> m_entities;
+			std::unordered_set<entity_t> m_killList;
 
 	};
 
diff --git a/include/fggl/phys/bullet/types.hpp b/include/fggl/phys/bullet/types.hpp
index 78896b78d3e84526bda061f6eab566531fe30fd5..04d8385edc93c41c441b6c0a58fc5434e6d011ad 100644
--- a/include/fggl/phys/bullet/types.hpp
+++ b/include/fggl/phys/bullet/types.hpp
@@ -62,6 +62,8 @@ namespace fggl::phys::bullet {
 			~BulletPhysicsEngine() override;
 
 			void step() override;
+
+			void onEntityDeath(ecs::entity_t entity);
 		private:
 			ecs3::World* m_ecs;
 			BulletConfiguration m_config;
diff --git a/include/fggl/phys/callbacks.hpp b/include/fggl/phys/callbacks.hpp
index cc14483fb26359aa77ef5a50566fa6357d74b70c..ac8a02b062cfcf65a97969cfe44b47d2094840a3 100644
--- a/include/fggl/phys/callbacks.hpp
+++ b/include/fggl/phys/callbacks.hpp
@@ -30,7 +30,7 @@
 
 namespace fggl::phys {
 
-	using CollisionCB = std::function<void(ecs3::entity_t)>;
+	using CollisionCB = std::function<void(ecs3::entity_t, ecs3::entity_t)>;
 
 	struct CollisionCallbacks {
 		constexpr static const char* name = "phys::Callbacks";