diff --git a/app/lib/Angel.coffee b/app/lib/Angel.coffee
index b4092c75f..efa47c03b 100644
--- a/app/lib/Angel.coffee
+++ b/app/lib/Angel.coffee
@@ -92,7 +92,7 @@ module.exports = class Angel extends CocoClass
         Backbone.Mediator.publish 'god:user-code-problem', problem: event.data.problem
       when 'world-load-progress-changed'
         Backbone.Mediator.publish 'god:world-load-progress-changed', progress: event.data.progress
-        unless event.data.progress is 1 or @work.preload or @work.headless or @work.synchronous or @deserializationQueue.length or @shared.firstWorld
+        unless event.data.progress is 1 or @work.preload or @work.headless or @work.synchronous or @deserializationQueue.length or (@shared.firstWorld and not @shared.spectate)
           @worker.postMessage func: 'serializeFramesSoFar'  # Stream it!
 
       # We have some or all of the frames serialized, so let's send the (partially?) simulated world to the Surface.
diff --git a/app/lib/God.coffee b/app/lib/God.coffee
index 8574aff99..cf1631689 100644
--- a/app/lib/God.coffee
+++ b/app/lib/God.coffee
@@ -24,6 +24,7 @@ module.exports = class God extends CocoClass
     @angelsShare =
       workerCode: options.workerCode or '/javascripts/workers/worker_world.js'  # Either path or function
       headless: options.headless  # Whether to just simulate the goals, or to deserialize all simulation results
+      spectate: options.spectate
       godNick: @nick
       workQueue: []
       firstWorld: true
diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee
index 2d8c45fb1..f2846c666 100644
--- a/app/lib/surface/Surface.coffee
+++ b/app/lib/surface/Surface.coffee
@@ -146,7 +146,6 @@ module.exports = Surface = class Surface extends CocoClass
     @showLevel()
     @updateState true if @loaded
     @onFrameChanged()
-    Backbone.Mediator.publish 'surface:world-set-up', {world: @world}
 
   showLevel: ->
     return if @destroyed
diff --git a/app/locale/en.coffee b/app/locale/en.coffee
index b1093ce0e..405ab7ec9 100644
--- a/app/locale/en.coffee
+++ b/app/locale/en.coffee
@@ -418,6 +418,7 @@
     skip_tutorial: "Skip (esc)"
     keyboard_shortcuts: "Key Shortcuts"
     loading_ready: "Ready!"
+    loading_start: "Start Level"
     tip_insert_positions: "Shift+Click a point on the map to insert it into the spell editor."
     tip_toggle_play: "Toggle play/paused with Ctrl+P."
     tip_scrub_shortcut: "Ctrl+[ and Ctrl+] rewind and fast-forward."
diff --git a/app/models/Level.coffee b/app/models/Level.coffee
index 2f1df8a50..348091b6a 100644
--- a/app/models/Level.coffee
+++ b/app/models/Level.coffee
@@ -108,7 +108,6 @@ module.exports = class Level extends CocoModel
       for original, placeholderComponent of placeholders when not placeholdersUsed[original]
         levelThang.components.push placeholderComponent
 
-
   sortSystems: (levelSystems, systemModels) ->
     [sorted, originalsSeen] = [[], {}]
     visit = (system) ->
diff --git a/app/schemas/subscriptions/surface.coffee b/app/schemas/subscriptions/surface.coffee
index 0ca5a6a92..ba00491e5 100644
--- a/app/schemas/subscriptions/surface.coffee
+++ b/app/schemas/subscriptions/surface.coffee
@@ -121,9 +121,6 @@ module.exports =  # /app/lib/surface
   'sprite:dragged': spriteMouseEventSchema
   'sprite:mouse-up': spriteMouseEventSchema
 
-  'surface:world-set-up': c.object {},
-    world: {type: 'object'}
-
   'surface:frame-changed': c.object {required: ['frame', 'world', 'progress']},
     frame: {type: 'number', minimum: 0}
     world: {type: 'object'}
diff --git a/app/styles/play/level/loading.sass b/app/styles/play/level/loading.sass
index 3dac90ec8..697694ad7 100644
--- a/app/styles/play/level/loading.sass
+++ b/app/styles/play/level/loading.sass
@@ -56,6 +56,9 @@
       position: relative
       z-index: 2
 
+    .start-level-button
+      margin-top: 10px
+
   .left-wing, .right-wing
     width: 100%
     height: 100%
diff --git a/app/templates/play/level/level_loading.jade b/app/templates/play/level/level_loading.jade
index 895524bce..23cc615ab 100644
--- a/app/templates/play/level/level_loading.jade
+++ b/app/templates/play/level/level_loading.jade
@@ -43,3 +43,4 @@
 
   .errors
   
+  button.start-level-button.btn.btn-lg.btn-success.secret(data-i18n="play_level.loading_start") Start Level
diff --git a/app/views/game-menu/InventoryView.coffee b/app/views/game-menu/InventoryView.coffee
index 5b93864af..f59f8269f 100644
--- a/app/views/game-menu/InventoryView.coffee
+++ b/app/views/game-menu/InventoryView.coffee
@@ -310,7 +310,7 @@ module.exports = class InventoryView extends CocoView
     necessaryGear = gearByLevel[@options.levelID]
     for slot, item of necessaryGear ? {}
       @equipment[slot] ?= gear[item]
-    @allowedItems = _.values gear if necessaryGear  # If it's one of these levels, don't show the extra items.
+    @allowedItems = _.union(_.values(gear), _.values(@equipment)) if necessaryGear  # If it's one of these levels, don't show the extra items.
 
   onHeroSelectionUpdated: (e) ->
     @selectedHero = e.hero
diff --git a/app/views/play/SpectateView.coffee b/app/views/play/SpectateView.coffee
index 05f97f20e..68ad72d0e 100644
--- a/app/views/play/SpectateView.coffee
+++ b/app/views/play/SpectateView.coffee
@@ -44,7 +44,6 @@ module.exports = class SpectateLevelView extends RootView
     'god:new-world-created': 'onNewWorld'
     'god:streaming-world-updated': 'onNewWorld'
     'god:infinite-loop': 'onInfiniteLoop'
-    'surface:world-set-up': 'onSurfaceSetUpNewWorld'
     'level:next-game-pressed': 'onNextGamePressed'
     'level:started': 'onLevelStarted'
     'level:loading-view-unveiled': 'onLoadingViewUnveiled'
@@ -84,7 +83,7 @@ module.exports = class SpectateLevelView extends RootView
       opponentSessionID: @sessionTwo
       spectateMode: true
       team: @getQueryVariable('team')
-    @god = new God maxAngels: 1
+    @god = new God maxAngels: 1, spectate: true
 
   getRenderData: ->
     c = super()
@@ -161,12 +160,14 @@ module.exports = class SpectateLevelView extends RootView
       @session.set 'multiplayer', false
 
   onLevelStarted: (e) ->
-    @loadingView?.unveil()
+    go = => @loadingView?.unveil()
+    _.delay go, 1000
 
   onLoadingViewUnveiled: (e) ->
     # Don't remove it; we want its decoration around on large screens.
     #@removeSubView @loadingView
     #@loadingView = null
+    Backbone.Mediator.publish 'level:set-playing', playing: true
 
   onSupermodelLoadedOne: =>
     @modelsLoaded ?= 0
@@ -229,13 +230,6 @@ module.exports = class SpectateLevelView extends RootView
     volume = 1.0 unless volume?
     Backbone.Mediator.publish 'level:set-volume', volume: volume
 
-  onSurfaceSetUpNewWorld: ->
-    return if @alreadyLoadedState
-    @alreadyLoadedState = true
-    state = @originalSessionState
-    if state.playing?
-      Backbone.Mediator.publish 'level:set-playing', playing: state.playing
-
   register: -> return
 
   onSessionWillSave: (e) ->
@@ -259,6 +253,7 @@ module.exports = class SpectateLevelView extends RootView
     return if @headless
     scripts = @world.scripts  # Since these worlds don't have scripts, preserve them.
     @world = e.world
+    @world.scripts = scripts
     thangTypes = @supermodel.getModels(ThangType)
     startFrame = @lastWorldFramesLoaded ? 0
     if @world.frames.length is @world.totalFrames  # Finished loading
diff --git a/app/views/play/level/LevelLoadingView.coffee b/app/views/play/level/LevelLoadingView.coffee
index 9d8ec311c..18e387d85 100644
--- a/app/views/play/level/LevelLoadingView.coffee
+++ b/app/views/play/level/LevelLoadingView.coffee
@@ -5,7 +5,11 @@ module.exports = class LevelLoadingView extends CocoView
   id: 'level-loading-view'
   template: template
 
+  events:
+    'click .start-level-button': 'onClickStartLevel'
+
   onLoaded: ->
+
   afterRender: ->
     @$el.find('.tip.rare').remove() if _.random(1, 10) < 9
     tips = @$el.find('.tip').addClass('to-remove')
@@ -19,12 +23,12 @@ module.exports = class LevelLoadingView extends CocoView
     ready = $.i18n.t('play_level.loading_ready', defaultValue: 'Ready!')
     @$el.find('#tip-wrapper .tip').addClass('ready').text ready
     Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'level_loaded', volume: 0.75  # old: loading_ready
+    @$el.find('.start-level-button').removeClass 'secret'
+
+  onClickStartLevel: (e) ->
+    @unveil()
 
   unveil: ->
-    _.delay @reallyUnveil, 1000
-
-  reallyUnveil: =>
-    return if @destroyed
     @$el.addClass 'unveiled'
     loadingDetails = @$el.find('.loading-details')
     duration = parseFloat loadingDetails.css 'transition-duration'
diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee
index 66aa6cd61..ad8842bcc 100644
--- a/app/views/play/level/PlayLevelView.coffee
+++ b/app/views/play/level/PlayLevelView.coffee
@@ -60,7 +60,6 @@ module.exports = class PlayLevelView extends RootView
     'level:reload-thang-type': 'onLevelReloadThangType'
     'level:play-next-level': 'onPlayNextLevel'
     'level:edit-wizard-settings': 'showWizardSettingsModal'
-    'surface:world-set-up': 'onSurfaceSetUpNewWorld'
     'level:session-will-save': 'onSessionWillSave'
     'level:started': 'onLevelStarted'
     'level:loading-view-unveiled': 'onLoadingViewUnveiled'
@@ -323,12 +322,12 @@ module.exports = class PlayLevelView extends RootView
     if @otherSession
       # TODO: colorize name and cloud by team, colorize wizard by user's color config
       @surface.createOpponentWizard id: @otherSession.get('creator'), name: @otherSession.get('creatorName'), team: @otherSession.get('team'), levelSlug: @level.get('slug'), codeLanguage: @otherSession.get('submittedCodeLanguage')
-    @loadingView?.unveil()
 
   onLoadingViewUnveiled: (e) ->
     @loadingView.$el.remove()
     @removeSubView @loadingView
     @loadingView = null
+    @restoreSessionState()
     unless @isEditorPreview
       @loadEndTime = new Date()
       loadDuration = @loadEndTime - @loadStartTime
@@ -336,7 +335,7 @@ module.exports = class PlayLevelView extends RootView
       application.tracker?.trackEvent 'Finished Level Load', level: @levelID, label: @levelID, loadDuration: loadDuration, ['Google Analytics']
       application.tracker?.trackTiming loadDuration, 'Level Load Time', @levelID, @levelID
 
-  onSurfaceSetUpNewWorld: ->
+  restoreSessionState: ->
     return if @alreadyLoadedState
     @alreadyLoadedState = true
     state = @originalSessionState