diff --git a/lib/tiny.lua b/lib/tiny.lua
index 290f938..cbf602f 100644
--- a/lib/tiny.lua
+++ b/lib/tiny.lua
@@ -478,8 +478,6 @@ end
 --- Adds an Entity to the world.
 -- Also call this on Entities that have changed Components such that they
 -- match different Filters. Returns the Entity.
--- TODO: Track entity age when debugging?
--- TODO: Track debugName field when debugging?
 function tiny.addEntity(world, entity)
     local e2c = world.entitiesToChange
     e2c[#e2c + 1] = entity
@@ -487,37 +485,18 @@ function tiny.addEntity(world, entity)
 end
 tiny_addEntity = tiny.addEntity
 
-if tinyTrackEntityAges then
+function tiny._wrapAddEntity(f)
     local wrapped = tiny.addEntity
     function tiny.addEntity(world, entity)
-        local added = wrapped(world, entity)
-        added[ENTITY_INIT_MS] = getCurrentTimeMilliseconds()
-        return added
-    end
-    tiny_addEntity = tiny.addEntity
-end
-
-if tinyWarnWhenNonDataOnEntities then
-    local wrapped = tiny.addEntity
-    function tiny.addEntity(world, entity)
-        local added = wrapped(world, entity)
-        local nonDataType = checkForNonData(added)
-        if nonDataType then
-            print("Detected non-data type '" .. nonDataType .. "' on entity")
-        end
-        return added
+        local ret = wrapped(world, entity)
+        f(world, entity)
+        return ret
     end
     tiny_addEntity = tiny.addEntity
 end
 
 --- Adds a System to the world. Returns the System.
 function tiny.addSystem(world, system)
-    if tinyLogSystemChanges then
-        print("addSystem '" .. (system.name or "unnamed") .. "'")
-    end
-    if system.world ~= nil then
-        error("System " .. system.name .. " already belongs to a World.")
-    end
     local s2a = world.systemsToAdd
     s2a[#s2a + 1] = system
     system.world = world
@@ -552,9 +531,6 @@ tiny_removeEntity = tiny.removeEntity
 
 --- Removes a System from the world. Returns the System.
 function tiny.removeSystem(world, system)
-    if tinyLogSystemChanges then
-        print("removeSystem '" .. (system.name or "unnamed") .. "'")
-    end
     if system.world ~= world then
         error("System " .. system.name .. " does not belong to this World.")
     end
@@ -616,10 +592,6 @@ function tiny_manageSystems(world)
         end
         s2r[i] = nil
 
-        -- Clean up System
-        if tinyLogSystemChanges then
-            print("Cleaning up system '" .. (system.name or "unnamed") .. "'")
-        end
         system.world = nil
         system.entities = nil
         system.indices = nil
@@ -814,7 +786,6 @@ function tiny.update(world, dt, filter)
         end
     end
 
-    local tinyLogSystemUpdateTime = tinyLogSystemUpdateTime
     --  Iterate through Systems IN ORDER
     for i = 1, #systems do
         local system = systems[i]
@@ -822,7 +793,6 @@ function tiny.update(world, dt, filter)
             -- Update Systems that have an update method (most Systems)
             local update = system.update
             if update then
-                local currentMs = tinyLogSystemUpdateTime and getCurrentTimeMilliseconds()
                 local interval = system.interval
                 if interval then
                     local bufferedTime = (system.bufferedTime or 0) + dt
@@ -834,18 +804,11 @@ function tiny.update(world, dt, filter)
                 else
                     update(system, dt)
                 end
-                if tinyLogSystemUpdateTime then
-                    local endTimeMs = getCurrentTimeMilliseconds()
-                    print(tostring(endTimeMs - currentMs) .. "ms taken to update system '" .. system.name .. "'")
-                end
             end
 
             system.modified = false
         end
     end
-    if tinyLogSystemUpdateTime then
-        print("")
-    end
 
     -- Iterate through Systems IN ORDER AGAIN
     for i = 1, #systems do
diff --git a/tiny-debug.lua b/tiny-debug.lua
index c1c0956..0b4ef8c 100644
--- a/tiny-debug.lua
+++ b/tiny-debug.lua
@@ -1,21 +1,58 @@
-tinyTrackEntityAges = false
-tinyLogSystemUpdateTime = false
-tinyLogSystemChanges = false
-tinyWarnWhenNonDataOnEntities = false
+local tiny = require("lib.tiny")
+require("lib.inspect")
 
-getCurrentTimeMilliseconds = function()
+---@class TinyDebug
+local tinyDebug = {}
+
+-- TODO: Track some debugName field when debugging?
+
+local function worldMetaIndex()
+    local worldMetaTable = getmetatable(tiny.world())
+    return worldMetaTable.__index
+end
+
+local function getCurrentTimeMilliseconds()
     return love.timer.getTime() * 1000
 end
 
-ENTITY_INIT_MS = { "ENTITY_INIT_MS" }
-if tinyTrackEntityAges then
-    function tinyGetEntityAgeMs(entity)
-        return entity[ENTITY_INIT_MS]
+--- Only applies to already-created systems!
+--- Performs a `world:refresh()` to ensure that all added systems are installed.
+---@param world World
+function tinyDebug.logSystemUpdateTime(world)
+    world:refresh()
+    local systems = world.systems
+    for i = 1, #systems do
+        local system = systems[i]
+        local wrappedUpdate = system.update
+        system.update = function(...)
+            local currentMs = getCurrentTimeMilliseconds()
+            local ret = wrappedUpdate(...)
+            local endTimeMs = getCurrentTimeMilliseconds()
+            print(tostring(endTimeMs - currentMs) .. "ms taken to update system '" .. (system.name or i) .. "'")
+            return ret
+        end
     end
 end
 
-if tinyWarnWhenNonDataOnEntities then
-    function checkForNonData(e, nested, tableCache)
+function tinyDebug.trackEntityAges()
+    ENTITY_INIT_MS = { "ENTITY_INIT_MS" }
+    -- tiny._trackEntityAges = true
+
+    tiny._wrapAddEntity(function(_, entity)
+        entity[ENTITY_INIT_MS] = getCurrentTimeMilliseconds()
+    end)
+end
+
+--- Throws an error if trackEntityAges() has not been called
+function tinyDebug.getEntityAgeMs(entity)
+    if ENTITY_INIT_MS == nil then
+        error("trackEntityAges() has not been enabled!")
+    end
+    return entity[ENTITY_INIT_MS]
+end
+
+function tinyDebug.warnWhenNonDataOnEntities()
+    local function checkForNonData(e, nested, tableCache)
         nested = nested or false
         tableCache = tableCache or {}
 
@@ -39,4 +76,54 @@ if tinyWarnWhenNonDataOnEntities then
             return valType
         end
     end
+
+    tiny._wrapAddEntity(function(_, entity)
+        local nonDataType = checkForNonData(entity)
+        if nonDataType then
+            print("Detected non-data type '" .. nonDataType .. "' on entity")
+        end
+    end)
 end
+
+function tinyDebug.throwOnUnusedEntities()
+    ---@return boolean
+    local function entityMatchesASystem(world, entity)
+        local systems = world.systems
+        for i = 1, #systems do
+            local system = systems[i]
+            local filter = system.filter
+            if filter and filter(system, entity) then
+                return true
+            end
+        end
+
+        return false
+    end
+
+    ---@return Entity[]
+    local function anyUnmatchedEntities(world)
+        local unmatchedEntities = {}
+        local el = world.entities
+
+        for i = 1, #el do
+            if not entityMatchesASystem(world, el[i]) then
+                unmatchedEntities[#unmatchedEntities + 1] = el[i]
+            end
+        end
+
+        return unmatchedEntities
+    end
+
+    local tinyUpdate = tiny.update
+    worldMetaIndex().update = function(world, dt, filter)
+        local ret = tinyUpdate(world, dt, filter)
+        local ecAll = world:getEntityCount()
+        local unmatchedEntities = anyUnmatchedEntities(world)
+        if #unmatchedEntities ~= 0  then
+            error("UNUSED ENTITY COUNT: " .. #unmatchedEntities .. "/" .. ecAll .. " " .. Inspect(unmatchedEntities, { depth = 2 }))
+        end
+        return ret
+    end
+end
+
+return tinyDebug