diff --git a/app/models/Level.coffee b/app/models/Level.coffee
index 1336b8594..5f6ff797c 100644
--- a/app/models/Level.coffee
+++ b/app/models/Level.coffee
@@ -23,7 +23,7 @@ module.exports = class Level extends CocoModel
 
     # Figure out ThangTypes' Components
     tmap = {}
-    tmap[t.thangType] = true for t in o.thangs
+    tmap[t.thangType] = true for t in o.thangs ? []
     o.thangTypes = (original: tt.get('original'), name: tt.get('name'), components: $.extend(true, [], tt.get('components')) for tt in supermodel.getModels ThangType when tmap[tt.get('original')] or tt.isFullyLoaded())
     @sortThangComponents o.thangTypes, o.levelComponents, 'ThangType'
     @fillInDefaultComponentConfiguration o.thangTypes, o.levelComponents
diff --git a/app/schemas/subscriptions/editor.coffee b/app/schemas/subscriptions/editor.coffee
index 852429b6b..db490f17c 100644
--- a/app/schemas/subscriptions/editor.coffee
+++ b/app/schemas/subscriptions/editor.coffee
@@ -24,11 +24,9 @@ module.exports =
   'editor:edit-level-thang': c.object {required: ['thangID']},
     thangID: {type: 'string'}
 
-  'editor:level-thang-edited': c.object {required: ['thangID', 'thangData']},
-    thangID: {type: 'string'}
+  'editor:level-thang-done-editing': c.object {required: ['thangData', 'oldPath']},
     thangData: {type: 'object'}
-
-  'editor:level-thang-done-editing': c.object {}
+    oldPath: {type: 'string'}
 
   'editor:thangs-edited': c.object {required: ['thangs']},
     thangs: c.array {}, {type: 'object'}
diff --git a/app/styles/editor/level/thangs-tab-view.sass b/app/styles/editor/level/thangs-tab-view.sass
index 9966135fb..ad8f66681 100644
--- a/app/styles/editor/level/thangs-tab-view.sass
+++ b/app/styles/editor/level/thangs-tab-view.sass
@@ -54,6 +54,9 @@
       outline: thin
       border: none
       padding-top: 0
+      
+    .treema-schema-select
+      display: none
     
     .treema-children .treema-row *
       cursor: pointer !important
diff --git a/app/views/editor/level/thangs/LevelThangEditView.coffee b/app/views/editor/level/thangs/LevelThangEditView.coffee
index fd3bafaf7..635b6b9c4 100644
--- a/app/views/editor/level/thangs/LevelThangEditView.coffee
+++ b/app/views/editor/level/thangs/LevelThangEditView.coffee
@@ -26,7 +26,7 @@ module.exports = class LevelThangEditView extends CocoView
     @world = options.world
     @thangData = $.extend true, {}, options.thangData ? {}
     @level = options.level
-    @oldID = @thangData.id
+    @oldPath = options.oldPath
 
   getRenderData: (context={}) ->
     context = super(context)
@@ -57,12 +57,8 @@ module.exports = class LevelThangEditView extends CocoView
     window.input = input
     @hideLoading()
 
-  saveThang: (e) ->
-    # Make sure it validates first?
-    Backbone.Mediator.publish 'editor:level-thang-edited', thangData: $.extend(true, {}, @thangData), thangID: @oldID
-
   navigateToAllThangs: ->
-    Backbone.Mediator.publish 'editor:level-thang-done-editing', {}
+    Backbone.Mediator.publish 'editor:level-thang-done-editing', {thangData: $.extend(true, {}, @thangData), oldPath: @oldPath}
 
   toggleNameEdit: ->
     link = @$el.find '#thang-name-link'
@@ -73,7 +69,6 @@ module.exports = class LevelThangEditView extends CocoView
     link.find('span, input').toggle()
     input.select() unless wasEditing
     @thangData.id = span.text()
-    @saveThang()
 
   toggleTypeEdit: ->
     link = @$el.find '#thang-type-link'
@@ -87,8 +82,6 @@ module.exports = class LevelThangEditView extends CocoView
     thangType = _.find @supermodel.getModels(ThangType), (m) -> m.get('name') is thangTypeName
     if thangType and wasEditing
       @thangData.thangType = thangType.get('original')
-    @saveThang()
 
   onComponentsChanged: (components) =>
     @thangData.components = components
-    @saveThang()
diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee
index a6ab707c1..49bcd1948 100644
--- a/app/views/editor/level/thangs/ThangsTabView.coffee
+++ b/app/views/editor/level/thangs/ThangsTabView.coffee
@@ -33,7 +33,6 @@ module.exports = class ThangsTabView extends CocoView
     'surface:mouse-over': 'onSurfaceMouseOver'
     'surface:mouse-out': 'onSurfaceMouseOut'
     'editor:edit-level-thang': 'editThang'
-    'editor:level-thang-edited': 'onLevelThangEdited'
     'editor:level-thang-done-editing': 'onLevelThangDoneEditing'
     'editor:view-switched': 'onViewSwitched'
     'sprite:dragged': 'onSpriteDragged'
@@ -110,7 +109,7 @@ module.exports = class ThangsTabView extends CocoView
     $(window).on 'resize', @onWindowResize
     @addThangsView = @insertSubView new AddThangsView world: @world
     @buildInterface() # refactor to not have this trigger when this view re-renders?
-    if @thangsTreema.data.length
+    if _.keys(@thangsTreema.data).length
       @$el.find('#canvas-overlay').css('display', 'none')
 
   onFilterExtantThangs: (e) ->
@@ -128,10 +127,24 @@ module.exports = class ThangsTabView extends CocoView
   buildInterface: (e) ->
     @level = e.level if e
 
-    data = $.extend(true, {}, @level.attributes)
+    data = $.extend(true, [], @level.attributes.thangs ? [])
+    thangsObject = @groupThangs(data)
+
+    schema = {
+      type: 'object'
+      format: 'thangs-folder'
+      additionalProperties: {
+        anyOf: [
+          Level.schema.properties.thangs.items
+          { $ref: '#' }
+        ]
+      }
+    }
+
     treemaOptions =
-      schema: Level.schema.properties.thangs
-      data: data.thangs
+      schema: schema
+      data: thangsObject
+      skipValidation: true
       supermodel: @supermodel
       callbacks:
         change: @onThangsChanged
@@ -140,7 +153,7 @@ module.exports = class ThangsTabView extends CocoView
       readOnly: true
       nodeClasses:
         thang: ThangNode
-        array: ThangsNode
+        'thangs-folder': ThangsFolderNode
       world: @world
 
     @thangsTreema = @$el.find('#thangs-treema').treema treemaOptions
@@ -151,7 +164,7 @@ module.exports = class ThangsTabView extends CocoView
     thangsHeaderHeight = $('#thangs-header').height()
     oldHeight = $('#thangs-list').height()
     $('#thangs-list').height(oldHeight - thangsHeaderHeight)
-    if data.thangs?.length
+    if data?.length
       @$el.find('.generate-terrain-button').hide()
 
   initSurface: ->
@@ -226,7 +239,9 @@ module.exports = class ThangsTabView extends CocoView
     @surface.camera.dragDisabled = false
     return unless @selectedExtantThang and e.thang?.id is @selectedExtantThang?.id
     pos = @selectedExtantThang.pos
-    path = "id=#{@selectedExtantThang.id}/components/original=#{LevelComponent.PhysicalID}"
+    
+    thang = _.find(@level.get('thangs') ? [], {id: @selectedExtantThang.id})
+    path = "#{@pathForThang(thang)}/components/original=#{LevelComponent.PhysicalID}"
     physical = @thangsTreema.get path
     return if not physical or (physical.config.pos.x is pos.x and physical.config.pos.y is pos.y)
     @thangsTreema.set path + '/config/pos', x: pos.x, y: pos.y, z: pos.z
@@ -237,8 +252,9 @@ module.exports = class ThangsTabView extends CocoView
 
   onRandomTerrainGenerated: (e) ->
     @thangsBatch = []
-    nonRandomThangs = (thang for thang in @thangsTreema.get('') when not /Random/.test thang.id)
-    @thangsTreema.set '', nonRandomThangs
+    @hush = true
+    nonRandomThangs = (thang for thang in @flattenThangs(@thangsTreema.data) when not /Random/.test thang.id)
+    @thangsTreema.set '', @groupThangs(nonRandomThangs)
 
     listening = {}
     for thang in e.thangs
@@ -251,7 +267,8 @@ module.exports = class ThangsTabView extends CocoView
         @listenToOnce @addThangType, 'build-complete', @onThangsChanged
           
       @addThang @addThangType, thang.pos, true
-    @batchInsert()
+    @hush = false
+    @onThangsChanged()
     @selectAddThangType null
 
 
@@ -270,14 +287,8 @@ module.exports = class ThangsTabView extends CocoView
       # We clicked on a Thang (or its Treema), so select the Thang
       @selectAddThang null, true
       @selectedExtantThangClickTime = new Date()
-      treemaThang = _.find @thangsTreema.childrenTreemas, (treema) => treema.data.id is @selectedExtantThang.id
-      if treemaThang
-        # Show the label above selected thang, notice that we may get here from thang-edit-view, so it will be selected but no label
-        # also covers selecting from Treema
-        @selectedExtantSprite.setNameLabel @selectedExtantSprite.thangType.get('name') + ': ' + @selectedExtantThang.id
-        if not treemaThang.isSelected()
-          treemaThang.select()
-          @thangsTreema.$el.scrollTop(@thangsTreema.$el.find('.treema-children .treema-selected')[0].offsetTop)
+      # Show the label above selected thang, notice that we may get here from thang-edit-view, so it will be selected but no label
+      @selectedExtantSprite.setNameLabel @selectedExtantSprite.thangType.get('name') + ': ' + @selectedExtantThang.id
     else if @addThangSprite
       # We clicked on the background when we had an add Thang selected, so add it
       @addThang @addThangType, @addThangSprite.thang.pos
@@ -398,12 +409,52 @@ module.exports = class ThangsTabView extends CocoView
 
   deleteSelectedExtantThang: (e) =>
     return if $(e.target).hasClass 'treema-node'
-    @thangsTreema.onDeletePressed e
-    @onTreemaThangSelected null, @thangsTreema.getSelectedTreemas()
+    thang = @getThangByID(@selectedExtantThang.id)
+    @thangsTreema.delete(@pathForThang(thang))
     Thang.resetThangIDs()  # TODO: find some way to do this when we delete from treema, too
+  
+  groupThangs: (thangs) ->
+    # array of thangs -> foldered thangs
+    grouped = {}
+    for thang in thangs
+      path = @folderForThang(thang)
+      obj = grouped
+      for key in path
+        obj[key] ?= {}
+        obj = obj[key]
+      obj[thang.id] = thang
+    grouped
+    
+  folderForThang: (thang) ->
+    thangType = @supermodel.getModelByOriginal ThangType, thang.thangType
+    [thangType.get('kind')]
+    
+  pathForThang: (thang) ->
+    folder = @folderForThang(thang)
+    folder.push thang.id
+    folder.join('/')
+  
+  flattenThangs: (thangs) ->
+    # foldered thangs -> array of thangs 
+    flattened = []
+    for key, value of thangs
+      if value.id and value.thangType
+        flattened.push value
+      else
+        flattened = flattened.concat @flattenThangs(value)
+    flattened
+    
+  populateFoldersForThang: (thang) ->
+    thangFolder = @folderForThang(thang)
+    prefix = ''
+    for segment in thangFolder
+      if prefix then prefix += '/'
+      prefix += segment
+      if not @thangsTreema.get(prefix) then @thangsTreema.set(prefix, {})
 
-  onThangsChanged: (e) =>
-    @level.set 'thangs', @thangsTreema.data
+  onThangsChanged: =>
+    return if @hush
+    @level.set 'thangs', @flattenThangs(@thangsTreema.data)
     return if @editThangView
     serializedLevel = @level.serialize @supermodel, null, true
     try
@@ -428,23 +479,21 @@ module.exports = class ThangsTabView extends CocoView
   onTreemaThangDoubleClicked: (e, treema) =>
     id = treema?.data?.id
     @editThang thangID: id if id
-
-  batchInsert: ->
-    @thangsTreema.set '', @thangsTreema.get('').concat(@thangsBatch)
-    @thangsBatch = []
+    
+  getThangByID: (id) -> _.find(@level.get('thangs') ? [], {id: id})
 
   addThang: (thangType, pos, batchInsert=false) ->
     @$el.find('.generate-terrain-button').hide()
     if batchInsert
       if thangType.get('name') is 'Hero Placeholder'
         thangID = 'Hero Placeholder'
-        return if @level.get('type', true) isnt 'hero' or @thangsTreema.get "id=#{thangID}"
+        return if @level.get('type', true) isnt 'hero' or @getThangByID(thangID)
       else
         thangID = "Random #{thangType.get('name')} #{@thangsBatch.length}"
     else
-      thangID = Thang.nextID(thangType.get('name'), @world) until thangID and not @thangsTreema.get "id=#{thangID}"
+      thangID = Thang.nextID(thangType.get('name'), @world) until thangID and not @getThangByID(thangID)
     if @cloneSourceThang
-      components = _.cloneDeep @thangsTreema.get "id=#{@cloneSourceThang.id}/components"
+      components = _.cloneDeep @getThangByID(@cloneSourceThang.id).components
     else if @level.get('type', true) is 'hero'
       components = []  # Load them all from default ThangType Components
     else
@@ -455,28 +504,29 @@ module.exports = class ThangsTabView extends CocoView
     thang = thangType: thangType.get('original'), id: thangID, components: components
     if batchInsert
       @thangsBatch.push thang
-    else
-      @thangsTreema.insert '', thang
+    @populateFoldersForThang(thang)
+    @thangsTreema.set(@pathForThang(thang), thang)
 
   editThang: (e) ->
     if e.target  # click event
       thangData = $(e.target).data 'thang-data'
     else  # Mediator event
-      window.thangsTreema = @thangsTreema
-      thangData = @thangsTreema.get "id=#{e.thangID}"
-    @editThangView = new LevelThangEditView thangData: thangData, level: @level, world: @world, supermodel: @supermodel  # supermodel needed for checkForMissingSystems
+      thangData = @getThangByID(e.thangID)
+    @editThangView = new LevelThangEditView thangData: thangData, level: @level, world: @world, supermodel: @supermodel, oldPath: @pathForThang(thangData)  # supermodel needed for checkForMissingSystems
     @insertSubView @editThangView
     @$el.find('>').hide()
     @editThangView.$el.show()
     Backbone.Mediator.publish 'editor:view-switched', {}
 
-  onLevelThangEdited: (e) ->
-    newThang = e.thangData
-    @thangsTreema.set "id=#{e.thangID}", newThang
-
   onLevelThangDoneEditing: (e) ->
     @removeSubView @editThangView
     @editThangView = null
+    newThang = e.thangData
+    @hush = true
+    @thangsTreema.delete e.oldPath
+    @populateFoldersForThang(newThang)
+    @thangsTreema.set(@pathForThang(newThang), newThang)
+    @hush = false
     @onThangsChanged()
     @$el.find('>').show()
 
@@ -507,10 +557,10 @@ module.exports = class ThangsTabView extends CocoView
   toggleThangsPalette: (e) ->
     $('#add-thangs-view').toggleClass('hide')
 
-class ThangsNode extends TreemaNode.nodeMap.array
-  valueClass: 'treema-array-replacement'
+class ThangsFolderNode extends TreemaNode.nodeMap.object
+  valueClass: 'treema-thangs-folder'
   nodeDescription: 'Thang'
-
+  
   getTrackedActionDescription: (trackedAction) ->
     trackedActionDescription = super(trackedAction)
     if trackedActionDescription is 'Edit ' + @nodeDescription
@@ -518,11 +568,9 @@ class ThangsNode extends TreemaNode.nodeMap.array
       if path[path.length-1] is 'pos'
         trackedActionDescription = 'Move Thang'
     trackedActionDescription
-
-  getChildren: ->
-    children = super(arguments...)
-    # TODO: add some filtering to only work with certain types of units at a time
-    return children
+    
+  buildValueForDisplay: (valEl, data) ->
+    @buildValueForDisplaySimply valEl, _.keys(data).length
 
 class ThangNode extends TreemaObjectNode
   valueClass: 'treema-thang'
@@ -531,13 +579,7 @@ class ThangNode extends TreemaObjectNode
   @thangKindMap: {}
   buildValueForDisplay: (valEl, data) ->
     pos = _.find(data.components, (c) -> c.config?.pos?)?.config.pos  # TODO: hack
-    s = "#{data.thangType}"
-    if isObjectID s
-      unless name = ThangNode.thangNameMap[s]
-        thangType = _.find @settings.supermodel.getModels(ThangType), (m) -> m.get('original') is s
-        name = ThangNode.thangNameMap[s] = thangType.get 'name'
-      s = name
-    s += ' - ' + data.id if data.id isnt s
+    s = ''
     if pos
       s += " (#{Math.round(pos.x)}, #{Math.round(pos.y)})"
     else