diff --git a/app/lib/AsyncCloner.coffee b/app/lib/AsyncCloner.coffee
new file mode 100644
index 000000000..7050d3452
--- /dev/null
+++ b/app/lib/AsyncCloner.coffee
@@ -0,0 +1,82 @@
+#CocoClass = require 'lib/CocoClass'
+#
+#module.exports = class AsyncCloner extends CocoClass
+#  constructor: (@source, @depth=2) ->
+#    # passing in a depth of 0 will just _.clone the first layer, and will result in 1 indexList
+#    super()
+#    @indexLists = []
+#    @initClone()
+#  
+#  initClone: () ->
+#    @target = AsyncCloner.cloneToDepth(@source, @depth)
+#    @indexLists = [_.keys(@target)] if _.isObject @target
+#    
+#  @cloneToDepth: (value, depth) ->
+#    value = _.clone(value)
+#    return value unless depth and _.isObject value
+#    value[key] = @cloneToDepth(value[key], depth-1) for key in _.keys value
+#    value
+#    
+#  clone: ->
+#    while @indexLists.length
+#      #console.log 'Clone loop:', JSON.stringify @indexLists
+#      @moveIndexForward() # fills or empties the index so @indexLists.length === @depth + 1
+#      break if @done()
+#      @cloneOne()
+#      @moveIndexForwardOne()
+#      break if @done() or @timeToSleep()
+#      
+#  moveIndexForward: ->
+#    while @indexLists.length
+#      nextValue = @getNextValue()
+#      if _.isObject(nextValue)
+#        if @indexLists.length <= @depth
+#          # push a new list if it's a collection
+#          @indexLists.push _.keys(nextValue)
+#          continue
+#        else 
+#          break # we done, the next value needs to be deep cloned
+#      #console.log 'Skipping:', @getNextPath()
+#      @moveIndexForwardOne() # move past this value otherwise
+#      #console.log '\tMoved index forward', JSON.stringify @indexLists
+#
+#  getNextValue: ->
+#    value = @target
+#    value = value[indexList[0]] for indexList in @indexLists
+#    value
+#    
+#  getNextParent: ->
+#    parent = @target
+#    parent = parent[indexList[0]] for indexList in @indexLists[...-1]
+#    parent
+#    
+#  getNextPath: ->
+#    (indexList[0] for indexList in @indexLists when indexList.length).join '.'
+#    
+#  moveIndexForwardOne: ->
+#    @indexLists[@indexLists.length-1].shift() # move the index forward one
+#    # if we reached the end of an index list, trim down through all finished lists
+#    while @indexLists.length and not @indexLists[@indexLists.length-1].length
+#      @indexLists.pop()
+#      @indexLists[@indexLists.length-1].shift() if @indexLists.length
+#
+#  cloneOne: ->
+#    if @indexLists.length isnt @depth + 1
+#      throw new Error('Cloner is in an invalid state!')
+#    parent = @getNextParent()
+#    key = @indexLists[@indexLists.length-1][0]
+#    parent[key] = _.cloneDeep parent[key]
+#    #console.log 'Deep Cloned:', @getNextPath()
+#
+#  done: -> not @indexLists.length
+#    
+#  timeToSleep: -> false
+
+
+###
+  Overall, the loop is:
+    Fill indexes if we need to to the depth we've cloned
+    Clone that one, popping it off the list.
+    If the last list is now empty, pop that list and every subsequent list if needed.
+    Check for doneness, or timeout.
+###
\ No newline at end of file
diff --git a/app/lib/LevelBus.coffee b/app/lib/LevelBus.coffee
index 18985716b..3f852471d 100644
--- a/app/lib/LevelBus.coffee
+++ b/app/lib/LevelBus.coffee
@@ -178,7 +178,7 @@ module.exports = class LevelBus extends Bus
     return unless @onPoint()
     state = @session.get('state')
     state.thangs ?= {}
-    methods = _.cloneDeep(e.methods)
+    methods = $.extend(true, {}, (e.methods))
     delete method.metrics.statements for methodName, method of methods
     state.thangs[e.thangID] = { methods: methods }
     @session.set('state', state)
diff --git a/app/lib/scripts/ScriptManager.coffee b/app/lib/scripts/ScriptManager.coffee
index 004f7d854..e879466f0 100644
--- a/app/lib/scripts/ScriptManager.coffee
+++ b/app/lib/scripts/ScriptManager.coffee
@@ -64,7 +64,7 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass
     @triggered = []
     @ended = []
     @noteGroupQueue = []
-    @scripts = _.cloneDeep(@originalScripts)
+    @scripts = $.extend(true, {}, @originalScripts)
 
   addScriptSubscriptions: ->
     idNum = 0
diff --git a/app/lib/sprites/SpriteParser.coffee b/app/lib/sprites/SpriteParser.coffee
index 15fe749cd..ce5b08f4b 100644
--- a/app/lib/sprites/SpriteParser.coffee
+++ b/app/lib/sprites/SpriteParser.coffee
@@ -1,7 +1,7 @@
 module.exports = class SpriteParser
   constructor: (@thangTypeModel) ->
     # Create a new ThangType, or work with one we've been building
-    @thangType = _.cloneDeep(@thangTypeModel.attributes.raw)
+    @thangType = $.extend(true, {}, @thangTypeModel.attributes.raw)
     @thangType ?= {}
     @thangType.shapes ?= {}
     @thangType.containers ?= {}
diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee
index 9fe0b45a1..74df2cc0e 100644
--- a/app/lib/surface/CocoSprite.coffee
+++ b/app/lib/surface/CocoSprite.coffee
@@ -56,7 +56,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
 
   constructor: (@thangType, options) ->
     super()
-    @options = _.extend(_.cloneDeep(@options), options)
+    @options = _.extend($.extend(true, {}, @options), options)
     @setThang @options.thang
     console.error @toString(), "has no ThangType!" unless @thangType
     @actionQueue = []
diff --git a/app/lib/surface/WizardSprite.coffee b/app/lib/surface/WizardSprite.coffee
index 702f79ce4..9a50abc0f 100644
--- a/app/lib/surface/WizardSprite.coffee
+++ b/app/lib/surface/WizardSprite.coffee
@@ -30,7 +30,7 @@ module.exports = class WizardSprite extends IndieSprite
 
   constructor: (thangType, options) ->
     if options?.isSelf
-      options.colorConfig = _.cloneDeep(me.get('wizard')?.colorConfig) or {}
+      options.colorConfig = $.extend(true, {}, me.get('wizard')?.colorConfig) or {}
     super thangType, options
     @isSelf = options.isSelf
     @targetPos = @thang.pos
@@ -67,7 +67,7 @@ module.exports = class WizardSprite extends IndieSprite
     @setNameLabel me.displayName() if @displayObject.visible  # not if we hid the wiz
     newColorConfig = me.get('wizard')?.colorConfig or {}
     shouldUpdate = not _.isEqual(newColorConfig, @options.colorConfig)
-    @options.colorConfig = _.cloneDeep(newColorConfig)
+    @options.colorConfig = $.extend(true, {}, newColorConfig)
     if shouldUpdate
       @setupSprite()
       @playAction(@currentAction)
diff --git a/app/lib/world/GoalManager.coffee b/app/lib/world/GoalManager.coffee
index b017482b8..d524a0386 100644
--- a/app/lib/world/GoalManager.coffee
+++ b/app/lib/world/GoalManager.coffee
@@ -90,7 +90,7 @@ module.exports = class GoalManager extends CocoClass
   # IMPLEMENTATION DETAILS
 
   addGoal: (goal) ->
-    goal = _.cloneDeep(goal)
+    goal = $.extend(true, {}, goal)
     goal.id = @nextGoalID++ if not goal.id
     return if @goalStates[goal.id]?
     @goals.push(goal)
diff --git a/app/models/ThangType.coffee b/app/models/ThangType.coffee
index bfd501adf..4373c768c 100644
--- a/app/models/ThangType.coffee
+++ b/app/models/ThangType.coffee
@@ -30,7 +30,7 @@ module.exports = class ThangType extends CocoModel
     return @actions or @buildActions()
 
   buildActions: ->
-    @actions = _.cloneDeep(@get('actions') or {})
+    @actions = $.extend(true, {}, @get('actions') or {})
     for name, action of @actions
       action.name = name
       for relatedName, relatedAction of action.relatedActions ? {}
diff --git a/app/views/account/wizard_settings_view.coffee b/app/views/account/wizard_settings_view.coffee
index 78f9cee6a..9a5e20e9c 100644
--- a/app/views/account/wizard_settings_view.coffee
+++ b/app/views/account/wizard_settings_view.coffee
@@ -69,7 +69,7 @@ module.exports = class WizardSettingsView extends CocoView
     colorGroup.find('.minicolors-swatch').toggle Boolean(enabled)
 
   updateColorSettings: (colorGroup) =>
-    wizardSettings = _.cloneDeep(me.get('wizard')) or {}
+    wizardSettings = $.extend(true, {}, me.get('wizard')) or {}
     wizardSettings.colorConfig ?= {}
     colorName = colorGroup.data('name')
     wizardSettings.colorConfig[colorName] ?= {}
diff --git a/app/views/editor/components/main.coffee b/app/views/editor/components/main.coffee
index fdcc55a6e..573ff054c 100644
--- a/app/views/editor/components/main.coffee
+++ b/app/views/editor/components/main.coffee
@@ -68,7 +68,7 @@ module.exports = class ThangComponentEditView extends CocoView
     treemaOptions =
       supermodel: @supermodel
       schema: { type: 'array', items: LevelComponent.schema.attributes }
-      data: (_.cloneDeep(c) for c in components)
+      data: ($.extend(true, {}, c) for c in components)
       callbacks: {select: @onSelectAddableComponent, enter: @onAddComponentEnterPressed }
       readOnly: true
       noSortable: true
@@ -155,7 +155,7 @@ module.exports = class ThangComponentEditView extends CocoView
 
 
   reportChanges: ->
-    @callback?(_.cloneDeep(@extantComponentsTreema.data))
+    @callback?($.extend(true, {}, @extantComponentsTreema.data))
 
 class ThangComponentsArrayNode extends TreemaArrayNode
   valueClass: 'treema-thang-components-array'
diff --git a/app/views/editor/level/fork_view.coffee b/app/views/editor/level/fork_view.coffee
index f807df6c7..9f8ffb229 100644
--- a/app/views/editor/level/fork_view.coffee
+++ b/app/views/editor/level/fork_view.coffee
@@ -25,7 +25,7 @@ module.exports = class LevelForkView extends View
   forkLevel: ->
     @showLoading()
     forms.clearFormAlerts(@$el)
-    newLevel = new Level(_.cloneDeep(@level.attributes))
+    newLevel = new Level($.extend(true, {}, @level.attributes))
     newLevel.unset '_id'
     newLevel.unset 'version'
     newLevel.unset 'creator'
diff --git a/app/views/editor/level/scripts_tab_view.coffee b/app/views/editor/level/scripts_tab_view.coffee
index f0088ad21..57192d31f 100644
--- a/app/views/editor/level/scripts_tab_view.coffee
+++ b/app/views/editor/level/scripts_tab_view.coffee
@@ -21,7 +21,7 @@ module.exports = class ScriptsTabView extends View
   onLevelLoaded: (e) ->
     @level = e.level
     @dimensions = @level.dimensions()
-    scripts = _.cloneDeep(@level.get('scripts') ? [])
+    scripts = $.extend(true, {}, @level.get('scripts') ? [])
     scripts = _.cloneDeep defaultScripts unless scripts.length
     treemaOptions =
       schema: Level.schema.get('properties').scripts
diff --git a/app/views/editor/level/system/add.coffee b/app/views/editor/level/system/add.coffee
index 3afd6e35c..4ea54e3ec 100644
--- a/app/views/editor/level/system/add.coffee
+++ b/app/views/editor/level/system/add.coffee
@@ -61,7 +61,7 @@ module.exports = class LevelSystemAddView extends View
       levelSystem =
         original: s.get('original') ? id
         majorVersion: s.get('version').major ? 0
-        config: _.cloneDeep(s.get('configSchema').default ? {})
+        config: $.extend(true, {}, s.get('configSchema').default ? {})
       @extantSystems.push levelSystem
       Backbone.Mediator.publish 'level-system-added', system: levelSystem
     @renderAvailableSystems()
diff --git a/app/views/editor/level/systems_tab_view.coffee b/app/views/editor/level/systems_tab_view.coffee
index a4b481754..00e175b7b 100644
--- a/app/views/editor/level/systems_tab_view.coffee
+++ b/app/views/editor/level/systems_tab_view.coffee
@@ -54,7 +54,7 @@ module.exports = class SystemsTabView extends View
     @buildSystemsTreema()
 
   buildSystemsTreema: ->
-    systems = _.cloneDeep(@level.get('systems') ? [])
+    systems = $.extend(true, {}, @level.get('systems') ? [])
     unless systems.length
       systems = @buildDefaultSystems()
       insertedDefaults = true
diff --git a/app/views/editor/thang/edit.coffee b/app/views/editor/thang/edit.coffee
index 23fa7f38d..a8a67ff39 100644
--- a/app/views/editor/thang/edit.coffee
+++ b/app/views/editor/thang/edit.coffee
@@ -40,7 +40,7 @@ module.exports = class ThangTypeEditView extends View
 
   constructor: (options, @thangTypeID) ->
     super options
-    @mockThang = _.cloneDeep(@mockThang)
+    @mockThang = $.extend(true, {}, @mockThang)
     @thangType = new ThangType(_id: @thangTypeID)
     @thangType.saveBackups = true
     @thangType.fetch()
@@ -320,7 +320,7 @@ module.exports = class ThangTypeEditView extends View
     @treema.set('/', @getThangData())
 
   getThangData: ->
-    data = _.cloneDeep(@thangType.attributes)
+    data = $.extend(true, {}, @thangType.attributes)
     data = _.pick data, (value, key) => not (key in ['components'])
 
   buildTreema: ->
diff --git a/app/views/play/level/modal/docs_modal.coffee b/app/views/play/level/modal/docs_modal.coffee
index 42aa98319..43ecfb070 100644
--- a/app/views/play/level/modal/docs_modal.coffee
+++ b/app/views/play/level/modal/docs_modal.coffee
@@ -25,7 +25,7 @@ module.exports = class DocsModal extends View
 
     @docs = specific.concat(general)
     marked.setOptions {gfm: true, sanitize: false, smartLists: true, breaks: false}
-    @docs = _.cloneDeep(@docs)
+    @docs = $.extend(true, {}, @docs)
     doc.html = marked(utils.i18n doc, 'body') for doc in @docs
     doc.name = (utils.i18n doc, 'name') for doc in @docs
     doc.slug = _.string.slugify(doc.name) for doc in @docs
diff --git a/app/views/play/level_view.coffee b/app/views/play/level_view.coffee
index 68bca74d8..aaa718703 100644
--- a/app/views/play/level_view.coffee
+++ b/app/views/play/level_view.coffee
@@ -167,7 +167,7 @@ module.exports = class PlayLevelView extends View
     @insertSubviews ladderGame: (@level.get('type') is "ladder")
     @initVolume()
     @session.on 'change:multiplayer', @onMultiplayerChanged, @
-    @originalSessionState = _.cloneDeep(@session.get('state'))
+    @originalSessionState = $.extend(true, {}, @session.get('state'))
     @register()
     @controlBar.setBus(@bus)
     @surface.showLevel()
diff --git a/app/views/play/spectate_view.coffee b/app/views/play/spectate_view.coffee
index d3c79b7ae..283c7dc57 100644
--- a/app/views/play/spectate_view.coffee
+++ b/app/views/play/spectate_view.coffee
@@ -160,7 +160,7 @@ module.exports = class SpectateLevelView extends View
     @insertSubviews ladderGame: @otherSession?
     @initVolume()
 
-    @originalSessionState = _.cloneDeep(@session.get('state'))
+    @originalSessionState = $.extend(true, {}, @session.get('state'))
     @register()
     @controlBar.setBus(@bus)
     @surface.showLevel()