diff --git a/app/lib/NameLoader.coffee b/app/lib/NameLoader.coffee
index 0dd798d80..e850bfc08 100644
--- a/app/lib/NameLoader.coffee
+++ b/app/lib/NameLoader.coffee
@@ -12,6 +12,6 @@ class NameLoader extends CocoClass
   loadedNames: (newNames) =>
     _.extend namesCache, newNames
     
-  getName: (id) -> namesCache[id]
+  getName: (id) -> namesCache[id].name
 
 module.exports = new NameLoader()
diff --git a/app/lib/SystemNameLoader.coffee b/app/lib/SystemNameLoader.coffee
new file mode 100644
index 000000000..f23dbe491
--- /dev/null
+++ b/app/lib/SystemNameLoader.coffee
@@ -0,0 +1,10 @@
+CocoClass = require 'lib/CocoClass'
+
+namesCache = {}
+
+class SystemNameLoader extends CocoClass
+  getName: (id) -> namesCache[id]?.name
+
+  setName: (system) -> namesCache[system.get('original')] = {name:system.get('name')}
+
+module.exports = new SystemNameLoader()
diff --git a/app/lib/deltas.coffee b/app/lib/deltas.coffee
index 9629139e8..de6d6c13b 100644
--- a/app/lib/deltas.coffee
+++ b/app/lib/deltas.coffee
@@ -1,3 +1,4 @@
+SystemNameLoader = require 'lib/SystemNameLoader'
 ### 
   Good-to-knows:
     dataPath: an array of keys that walks you up a JSON object that's being patched
@@ -73,7 +74,7 @@ expandFlattenedDelta = (delta, left, schema) ->
     humanKey = null
     childData = if i is delta.dataPath.length-1 and delta.action is 'added' then o[0] else childLeft
     humanKey ?= childData.name or childData.id if childData
-    humanKey ?= "#{childSchema.title} ##{key+1}" if childSchema.title and _.isNumber(key)
+    humanKey ?= SystemNameLoader.getName(childData?.original)
     humanKey ?= "#{childSchema.title}" if childSchema.title
     humanKey ?= _.string.titleize key
     humanPath.push humanKey
@@ -155,4 +156,4 @@ prunePath = (delta, path) ->
   else
     prunePath delta[path[0]], path.slice(1)
     keys = (k for k in _.keys(delta[path[0]]) when k isnt '_t')
-    delete delta[path[0]] if keys.length is 0
\ No newline at end of file
+    delete delta[path[0]] if keys.length is 0
diff --git a/app/models/LevelSystem.coffee b/app/models/LevelSystem.coffee
index 8123c6f79..ce92cd070 100644
--- a/app/models/LevelSystem.coffee
+++ b/app/models/LevelSystem.coffee
@@ -1,4 +1,5 @@
 CocoModel = require('./CocoModel')
+SystemNameLoader = require('lib/SystemNameLoader')
 
 module.exports = class LevelSystem extends CocoModel
   @className: "LevelSystem"
@@ -16,6 +17,7 @@ module.exports = class LevelSystem extends CocoModel
   onLoaded: ->
     super()
     @set 'js', @compile(@get 'code') unless @get 'js'
+    SystemNameLoader.setName @
 
   compile: (code) ->
     if @get('language') and @get('language') isnt 'coffeescript'
diff --git a/app/views/play/ladder/my_matches_tab.coffee b/app/views/play/ladder/my_matches_tab.coffee
index e3f0fc62a..a725a1b16 100644
--- a/app/views/play/ladder/my_matches_tab.coffee
+++ b/app/views/play/ladder/my_matches_tab.coffee
@@ -35,7 +35,7 @@ module.exports = class MyMatchesTabView extends CocoView
       for session in @sessions.models
         for match in session.get('matches') or []
           opponent = match.opponents[0]
-          @nameMap[opponent.userID] ?= nameMap[opponent.userID]
+          @nameMap[opponent.userID] ?= nameMap[opponent.userID].name
       @finishRendering()
 
     $.ajax('/db/user/-/names', {
diff --git a/server/commons/Handler.coffee b/server/commons/Handler.coffee
index 0a32a1205..9c6b165e0 100644
--- a/server/commons/Handler.coffee
+++ b/server/commons/Handler.coffee
@@ -373,3 +373,17 @@ module.exports = class Handler
     return done(validation) unless validation.valid
 
     document.save (err) -> done(err)
+
+  getPropertiesFromMultipleDocuments: (res, model, properties, ids) ->
+    query = model.find()
+    ids = ids.split(',') if _.isString ids
+    ids = _.uniq ids
+    for id in ids
+      return errors.badInput(res, "Given an invalid id: #{id}") unless Handler.isID(id)
+    query.where({'_id': { $in: ids} })
+    query.select(properties).exec (err, documents) ->
+      dict = {}
+      _.each documents, (document) ->
+        dict[document.id] = document
+      res.send dict
+      res.end()
diff --git a/server/users/user_handler.coffee b/server/users/user_handler.coffee
index 0073bf5ae..d33cc1642 100644
--- a/server/users/user_handler.coffee
+++ b/server/users/user_handler.coffee
@@ -120,34 +120,11 @@ UserHandler = class UserHandler extends Handler
       return @sendSuccess(res, @formatEntity(req, req.user, 256))
     super(req, res, id)
 
-  getNamesByIds: (req, res) ->
+  getNamesByIDs: (req, res) ->
     ids = req.query.ids or req.body.ids
-    ids = ids.split(',') if _.isString ids
-    ids = _.uniq ids
-
-    # TODO: Extend and repurpose this handler to return other public info about a user more flexibly,
-    #   say by a query parameter that lists public properties to return.
     returnWizard = req.query.wizard or req.body.wizard
-    query = if returnWizard then {name:1, wizard:1} else {name:1}
-
-    makeFunc = (id) ->
-      (callback) ->
-        User.findById(id, query).exec (err, document) ->
-          return done(err) if err
-          if document and returnWizard
-            callback(null, {name:document.get('name'), wizard:document.get('wizard') or {}})
-          else
-            callback(null, document?.get('name') or '')
-
-    funcs = {}
-    for id in ids
-      return errors.badInput(res, "Given an invalid id: #{id}") unless Handler.isID(id)
-      funcs[id] = makeFunc(id)
-
-    async.parallel funcs, (err, results) ->
-      return errors.serverError err if err
-      res.send results
-      res.end()
+    properties = if returnWizard then "name wizard" else "name"
+    @getPropertiesFromMultipleDocuments res, User, properties, ids
 
   nameToID: (req, res, name) ->
     User.findOne({nameLower:name.toLowerCase()}).exec (err, otherUser) ->
@@ -206,7 +183,7 @@ UserHandler = class UserHandler extends Handler
   getByRelationship: (req, res, args...) ->
     return @agreeToCLA(req, res) if args[1] is 'agreeToCLA'
     return @avatar(req, res, args[0]) if args[1] is 'avatar'
-    return @getNamesByIds(req, res) if args[1] is 'names'
+    return @getNamesByIDs(req, res) if args[1] is 'names'
     return @nameToID(req, res, args[0]) if args[1] is 'nameToID'
     return @getLevelSessions(req, res, args[0]) if args[1] is 'level.sessions'
     return @getCandidates(req, res) if args[1] is 'candidates'