diff --git a/app/assets/javascripts/workers/worker_world.js b/app/assets/javascripts/workers/worker_world.js
index 2ec1d73f8..7fa0d9794 100644
--- a/app/assets/javascripts/workers/worker_world.js
+++ b/app/assets/javascripts/workers/worker_world.js
@@ -363,7 +363,7 @@ self.runWorld = function runWorld(args) {
   for(var key in replacedLoDash)
     _[key] = replacedLoDash[key];
   self.postMessage({type: 'start-load-frames'});
-  self.world.loadFrames(self.onWorldLoaded, self.onWorldError, self.onWorldLoadProgress);
+  self.world.loadFrames(self.onWorldLoaded, self.onWorldError, self.onWorldLoadProgress, self.onWorldPreloaded);
 };
 
 self.serializeFramesSoFar = function serializeFramesSoFar() {
@@ -378,8 +378,9 @@ self.onWorldLoaded = function onWorldLoaded() {
   if(self.world.ended)
     self.goalManager.worldGenerationEnded();
   var goalStates = self.goalManager.getGoalStates();
+  var overallStatus = self.goalManager.checkOverallStatus();
   if(self.world.ended)
-    self.postMessage({type: 'end-load-frames', goalStates: goalStates});
+    self.postMessage({type: 'end-load-frames', goalStates: goalStates, overallStatus: overallStatus});
   var t1 = new Date();
   var diff = t1 - self.t0;
   if (self.world.headless)
@@ -416,6 +417,13 @@ self.onWorldLoaded = function onWorldLoaded() {
   }
 };
 
+self.onWorldPreloaded = function onWorldPreloaded() {
+  self.goalManager.worldGenerationEnded();
+  var goalStates = self.goalManager.getGoalStates();
+  var overallStatus = self.goalManager.checkOverallStatus();
+  self.postMessage({type: 'end-preload-frames', goalStates: goalStates, overallStatus: overallStatus});
+};
+
 self.onWorldError = function onWorldError(error) {
   if(error.isUserCodeProblem) {
     var errorKey = error.userInfo.key;
diff --git a/app/lib/Angel.coffee b/app/lib/Angel.coffee
index efa47c03b..94edc3088 100644
--- a/app/lib/Angel.coffee
+++ b/app/lib/Angel.coffee
@@ -66,7 +66,11 @@ module.exports = class Angel extends CocoClass
         clearTimeout @condemnTimeout
       when 'end-load-frames'
         clearTimeout @condemnTimeout
-        @beholdGoalStates event.data.goalStates  # Work ends here if we're headless.
+        @beholdGoalStates event.data.goalStates, event.data.overallStatus  # Work ends here if we're headless.
+      when 'end-preload-frames'
+        clearTimeout @condemnTimeout
+        @beholdGoalStates event.data.goalStates, event.data.overallStatus, true
+
 
       # We have to abort like an infinite loop if we see one of these; they're not really recoverable
       when 'non-user-code-problem'
@@ -105,9 +109,9 @@ module.exports = class Angel extends CocoClass
       else
         @log 'Received unsupported message:', event.data
 
-  beholdGoalStates: (goalStates) ->
+  beholdGoalStates: (goalStates, overallStatus, preload=false) ->
     return if @aborting
-    Backbone.Mediator.publish 'god:goals-calculated', goalStates: goalStates
+    Backbone.Mediator.publish 'god:goals-calculated', goalStates: goalStates, preload: preload, overallStatus: overallStatus
     @finishWork() if @shared.headless
 
   beholdWorld: (serialized, goalStates, startFrame, endFrame, streamingWorld) ->
diff --git a/app/lib/world/world.coffee b/app/lib/world/world.coffee
index 0cc3469de..43db43ca6 100644
--- a/app/lib/world/world.coffee
+++ b/app/lib/world/world.coffee
@@ -92,11 +92,11 @@ module.exports = class World
     (@runtimeErrors ?= []).push error
     (@unhandledRuntimeErrors ?= []).push error
 
-  loadFrames: (loadedCallback, errorCallback, loadProgressCallback, skipDeferredLoading, loadUntilFrame) ->
+  loadFrames: (loadedCallback, errorCallback, loadProgressCallback, preloadedCallback, skipDeferredLoading, loadUntilFrame) ->
     return if @aborted
     console.log 'Warning: loadFrames called on empty World (no thangs).' unless @thangs.length
     continueLaterFn = =>
-      @loadFrames(loadedCallback, errorCallback, loadProgressCallback, skipDeferredLoading, loadUntilFrame) unless @destroyed
+      @loadFrames(loadedCallback, errorCallback, loadProgressCallback, preloadedCallback, skipDeferredLoading, loadUntilFrame) unless @destroyed
     if @realTime and not @countdownFinished
       return setTimeout @finishCountdown(continueLaterFn), REAL_TIME_COUNTDOWN_DELAY
     t1 = now()
@@ -117,13 +117,15 @@ module.exports = class World
         for error in (@unhandledRuntimeErrors ? [])
           return unless errorCallback error  # errorCallback tells us whether the error is recoverable
         @unhandledRuntimeErrors = []
-    @finishLoadingFrames loadProgressCallback, loadedCallback
+    @finishLoadingFrames loadProgressCallback, loadedCallback, preloadedCallback
 
-  finishLoadingFrames: (loadProgressCallback, loadedCallback) ->
+  finishLoadingFrames: (loadProgressCallback, loadedCallback, preloadedCallback) ->
     unless @debugging
       @ended = true
       system.finish @thangs for system in @systems
-    unless @preloading
+    if @preloading
+      preloadedCallback()
+    else
       loadProgressCallback? 1
       loadedCallback()
 
diff --git a/app/schemas/subscriptions/god.coffee b/app/schemas/subscriptions/god.coffee
index 6121b838d..014594893 100644
--- a/app/schemas/subscriptions/god.coffee
+++ b/app/schemas/subscriptions/god.coffee
@@ -1,6 +1,6 @@
 c = require 'schemas/schemas'
 
-goalStatesSchema = 
+goalStatesSchema =
   type: 'object'
   additionalProperties:
     type: 'object'
@@ -41,6 +41,8 @@ module.exports =
 
   'god:goals-calculated': c.object {required: ['goalStates']},
     goalStates: goalStatesSchema
+    preload: {type: 'boolean'}
+    overallStatus: {type: ['string', 'null'], enum: ['success', 'failure', 'incomplete', null]}
 
   'god:world-load-progress-changed': c.object {required: ['progress']},
     progress: {type: 'number', minimum: 0, maximum: 1}
diff --git a/app/styles/play/level/tome/cast_button.sass b/app/styles/play/level/tome/cast_button.sass
index 1b1e2114f..5b50e7182 100644
--- a/app/styles/play/level/tome/cast_button.sass
+++ b/app/styles/play/level/tome/cast_button.sass
@@ -34,25 +34,37 @@
   .btn
     padding: 3px 10px
     height: 40px
+    font-size: 22px
 
   .submit-button
     margin-left: 20px
+    min-width: 150px
   
   .cast-button
     margin-left: 10px
+    min-width: 150px
     @include opacity(0.77)
   
     &:hover, &.castable
       @include opacity(1)
   
-  &:not(.winnable) .cast-button.castable
-    font-weight: bold
-    -webkit-animation-name: castablePulse
-    -webkit-animation-duration: 3s
-    -webkit-animation-iteration-count: infinite
+  &:not(.winnable)
 
-  &.winnable .submit-button
-    font-weight: bold
-    -webkit-animation-name: winnablePulse
-    -webkit-animation-duration: 3s
-    -webkit-animation-iteration-count: infinite
+    .cast-button.castable
+      font-weight: bold
+      -webkit-animation-name: castablePulse
+      -webkit-animation-duration: 3s
+      -webkit-animation-iteration-count: infinite
+
+    .submit-button
+      font-size: 16px
+
+  &.winnable
+    .submit-button
+      font-weight: bold
+      -webkit-animation-name: winnablePulse
+      -webkit-animation-duration: 3s
+      -webkit-animation-iteration-count: infinite
+
+    .cast-button
+      font-size: 16px
diff --git a/app/views/play/level/tome/CastButtonView.coffee b/app/views/play/level/tome/CastButtonView.coffee
index dafde5b0f..e30bac173 100644
--- a/app/views/play/level/tome/CastButtonView.coffee
+++ b/app/views/play/level/tome/CastButtonView.coffee
@@ -17,6 +17,7 @@ module.exports = class CastButtonView extends CocoView
     'real-time-multiplayer:joined-game': 'onJoinedRealTimeMultiplayerGame'
     'real-time-multiplayer:left-game': 'onLeftRealTimeMultiplayerGame'
     'goal-manager:new-goal-states': 'onNewGoalStates'
+    'god:goals-calculated': 'onGoalsCalculated'
 
   constructor: (options) ->
     super options
@@ -85,6 +86,10 @@ module.exports = class CastButtonView extends CocoView
     if @winnable
       @$el.find('.submit-button').show()  # In case we hid it, like on the first level.
 
+  onGoalsCalculated: (e) ->
+    return unless e.preload
+    @onNewGoalStates e
+
   updateCastButton: ->
     return if _.some @spells, (spell) => not spell.loaded