diff --git a/app/assets/main.html b/app/assets/main.html
index d2a3257b4..74737fd6b 100644
--- a/app/assets/main.html
+++ b/app/assets/main.html
@@ -31,9 +31,9 @@
 
   <link rel="shortcut icon" href="/images/favicon.ico">
   <link rel="stylesheet" href="/stylesheets/app.css">
-  <script src="/lib/ace/ace.js"></script>  
-  <!--[if IE 9]>     <script src="/javascripts/vendor_with_box2d.js"></script> <![endif]-->
-  <!--[if !IE]><!--> <script src="/javascripts/vendor.js"></script>            <!--<![endif]-->
+  <script src="/lib/ace/ace.js"></script>
+  <!--[if IE 9]><script src="/javascripts/box2d.js"></script><![endif]-->
+  <script src="/javascripts/vendor.js"></script>
   <script src="/javascripts/aether.js"></script>
   <script src="/javascripts/app.js"></script> <!-- it's all Backbone! -->
   
diff --git a/app/lib/Angel.coffee b/app/lib/Angel.coffee
index 94edc3088..39059ea7b 100644
--- a/app/lib/Angel.coffee
+++ b/app/lib/Angel.coffee
@@ -4,6 +4,7 @@
 {now} = require 'lib/world/world_utils'
 World = require 'lib/world/world'
 CocoClass = require 'lib/CocoClass'
+GoalManager = require 'lib/world/GoalManager'
 
 module.exports = class Angel extends CocoClass
   @nicks: ['Archer', 'Lana', 'Cyril', 'Pam', 'Cheryl', 'Woodhouse', 'Ray', 'Krieger']
@@ -210,6 +211,11 @@ module.exports = class Angel extends CocoClass
     @hireWorker() if rehire
 
   hireWorker: ->
+    unless Worker?
+      unless @initialized
+        @initialized = true
+        @doWork()
+      return null
     return if @worker
     @say 'Hiring worker.'
     @worker = new Worker @shared.workerCode
@@ -232,7 +238,7 @@ module.exports = class Angel extends CocoClass
     work.testWorld = testWorld = new World work.userCodeMap
     testWorld.loadFromLevel work.level
     if @shared.goalManager
-      testGM = new @shared.goalManager.constructor @testWorld
+      testGM = new GoalManager(testWorld)
       testGM.setGoals work.goals
       testGM.setCode work.userCodeMap
       testGM.worldGenerationWillBegin()
@@ -243,11 +249,12 @@ module.exports = class Angel extends CocoClass
 
     # If performance was really a priority in IE9, we would rework things to be able to skip this step.
     goalStates = testGM?.getGoalStates()
-    serialized = testWorld.serialize().serializedWorld
+    work.testWorld.goalManager.worldGenerationEnded() if work.testWorld.ended
+    serialized = testWorld.serialize()
     window.BOX2D_ENABLED = false
-    World.deserialize serialized, @angelsShare.worldClassMap, @shared.lastSerializedWorldFrames, @finishBeholdingWorld(goalStates)
+    World.deserialize serialized.serializedWorld, @shared.worldClassMap, @shared.lastSerializedWorldFrames, @finishBeholdingWorld(goalStates), serialized.startFrame, serialized.endFrame
     window.BOX2D_ENABLED = true
-    @shared.lastSerializedWorldFrames = serialized.frames
+    @shared.lastSerializedWorldFrames = serialized.serializedWorld.frames
 
   doSimulateWorld: (work) ->
     work.t1 = now()
@@ -258,6 +265,7 @@ module.exports = class Angel extends CocoClass
     i = 0
     while i < work.testWorld.totalFrames
       frame = work.testWorld.getFrame i++
+    Backbone.Mediator.publish 'god:world-load-progress-changed', progress: 1
     work.testWorld.ended = true
     system.finish work.testWorld.thangs for system in work.testWorld.systems
     work.t2 = now()
diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee
index 32bb91bf7..be9ef5a7b 100644
--- a/app/models/CocoModel.coffee
+++ b/app/models/CocoModel.coffee
@@ -19,6 +19,7 @@ class CocoModel extends Backbone.Model
     @on 'error', @onError, @
     @on 'add', @onLoaded, @
     @saveBackup = _.debounce(@saveBackup, 500)
+    console.debug = console.log unless console.debug # Needed for IE10 and earlier
 
   setProjection: (project) ->
     return if project is @project
diff --git a/app/views/play/level/tome/Spell.coffee b/app/views/play/level/tome/Spell.coffee
index af5349f6e..43bfb0faf 100644
--- a/app/views/play/level/tome/Spell.coffee
+++ b/app/views/play/level/tome/Spell.coffee
@@ -110,19 +110,22 @@ module.exports = class Spell
     unless aether
       console.error @toString(), 'couldn\'t find a spellThang with aether of', @thangs
       cb false
-    workerMessage =
-      function: 'hasChangedSignificantly'
-      a: (newSource ? @originalSource)
-      spellKey: @spellKey
-      b: (currentSource ? @source)
-      careAboutLineNumbers: true
-      careAboutLint: true
-    @worker.addEventListener 'message', (e) =>
-      workerData = JSON.parse e.data
-      if workerData.function is 'hasChangedSignificantly' and workerData.spellKey is @spellKey
-        @worker.removeEventListener 'message', arguments.callee, false
-        cb(workerData.hasChanged)
-    @worker.postMessage JSON.stringify(workerMessage)
+    if @worker
+      workerMessage =
+        function: 'hasChangedSignificantly'
+        a: (newSource ? @originalSource)
+        spellKey: @spellKey
+        b: (currentSource ? @source)
+        careAboutLineNumbers: true
+        careAboutLint: true
+      @worker.addEventListener 'message', (e) =>
+        workerData = JSON.parse e.data
+        if workerData.function is 'hasChangedSignificantly' and workerData.spellKey is @spellKey
+          @worker.removeEventListener 'message', arguments.callee, false
+          cb(workerData.hasChanged)
+      @worker.postMessage JSON.stringify(workerMessage)
+    else
+      cb(aether.hasChangedSignificantly((newSource ? @originalSource), (currentSource ? @source), true, true))
 
   createAether: (thang) ->
     aceConfig = me.get('aceConfig') ? {}
@@ -130,11 +133,12 @@ module.exports = class Spell
     skipProtectAPI = @skipProtectAPI or not writable
     aetherOptions = createAetherOptions functionName: @name, codeLanguage: @language, functionParameters: @parameters, skipProtectAPI: skipProtectAPI
     aether = new Aether aetherOptions
-    workerMessage =
-      function: 'createAether'
-      spellKey: @spellKey
-      options: aetherOptions
-    @worker.postMessage JSON.stringify workerMessage
+    if @worker
+      workerMessage =
+        function: 'createAether'
+        spellKey: @spellKey
+        options: aetherOptions
+      @worker.postMessage JSON.stringify workerMessage
     aether
 
   updateLanguageAether: (@language) ->
@@ -142,10 +146,11 @@ module.exports = class Spell
       spellThang.aether?.setLanguage @language
       spellThang.castAether = null
       Backbone.Mediator.publish 'tome:spell-changed-language', spell: @, language: @language
-    workerMessage =
-      function: 'updateLanguageAether'
-      newLanguage: @language
-    @worker.postMessage JSON.stringify workerMessage
+    if @worker
+      workerMessage =
+        function: 'updateLanguageAether'
+        newLanguage: @language
+      @worker.postMessage JSON.stringify workerMessage
     @transpile()
 
   toString: ->
diff --git a/app/views/play/level/tome/SpellView.coffee b/app/views/play/level/tome/SpellView.coffee
index aba6881cb..a2542ca08 100644
--- a/app/views/play/level/tome/SpellView.coffee
+++ b/app/views/play/level/tome/SpellView.coffee
@@ -400,19 +400,23 @@ module.exports = class SpellView extends CocoView
 
       @clearAetherDisplay()
       if codeHasChangedSignificantly and not codeIsAsCast
-        workerMessage =
-          function: 'transpile'
-          spellKey: @spell.spellKey
-          source: source
+        if @worker
+          workerMessage =
+            function: 'transpile'
+            spellKey: @spell.spellKey
+            source: source
 
-        @worker.addEventListener 'message', (e) =>
-          workerData = JSON.parse e.data
-          if workerData.function is 'transpile' and workerData.spellKey is @spell.spellKey
-            @worker.removeEventListener 'message', arguments.callee, false
-            aether.problems = workerData.problems
-            aether.raw = source
-            finishUpdatingAether(aether)
-        @worker.postMessage JSON.stringify(workerMessage)
+          @worker.addEventListener 'message', (e) =>
+            workerData = JSON.parse e.data
+            if workerData.function is 'transpile' and workerData.spellKey is @spell.spellKey
+              @worker.removeEventListener 'message', arguments.callee, false
+              aether.problems = workerData.problems
+              aether.raw = source
+              finishUpdatingAether(aether)
+          @worker.postMessage JSON.stringify(workerMessage)
+        else
+          aether.transpile source
+          finishUpdatingAether(aether)
       else
         finishUpdatingAether(aether)
 
diff --git a/app/views/play/level/tome/TomeView.coffee b/app/views/play/level/tome/TomeView.coffee
index 008953742..de1f10008 100644
--- a/app/views/play/level/tome/TomeView.coffee
+++ b/app/views/play/level/tome/TomeView.coffee
@@ -89,6 +89,7 @@ module.exports = class TomeView extends CocoView
     @cast()
 
   createWorker: ->
+    return null unless Worker?
     return new Worker('/javascripts/workers/aether_worker.js')
 
   generateTeamSpellMap: (spellObject) ->
diff --git a/config.coffee b/config.coffee
index 62d05b3d8..4f0ab82b7 100644
--- a/config.coffee
+++ b/config.coffee
@@ -25,9 +25,11 @@ exports.config =
           vendor[\/\\](?!scripts[\/\\]Box2d)
           |bower_components[\/\\](?!aether)
         )///
-        'javascripts/vendor_with_box2d.js': ///^(
-          vendor[\/\\]
-          |bower_components[\/\\](?!aether)  # include box2dweb for profiling (and for IE9...)
+        'javascripts/box2d.js': ///^( 
+          # Include box2dweb for profiling and IE9
+          # Vector renamed to Box2DVector to avoid name collisions
+          # TODO: move this to assets/lib since we're not really joining anything here?
+          (vendor[\/\\]scripts[\/\\]Box2dWeb-2.1.a.3) 
         )///
         'javascripts/lodash.js': ///^(
           (bower_components[\/\\]lodash[\/\\]dist[\/\\]lodash.js)
diff --git a/vendor/scripts/Box2dWeb-2.1.a.3.js b/vendor/scripts/Box2dWeb-2.1.a.3.js
index bca465363..6adc1337d 100644
--- a/vendor/scripts/Box2dWeb-2.1.a.3.js
+++ b/vendor/scripts/Box2dWeb-2.1.a.3.js
@@ -67,7 +67,7 @@ var Box2D = {};
 })(Box2D);
 
 //#TODO remove assignments from global namespace
-var Vector = Array;
+var Box2DVector = Array;
 var Vector_a2j_Number = Box2D.NVector;
 //package structure
 if (typeof(Box2D) === "undefined") Box2D = {};
@@ -1012,7 +1012,7 @@ Box2D.postDefs = [];
       tClip.id.features.incidentVertex = 1;
    }
    b2Collision.MakeClipPointVector = function () {
-      var r = new Vector(2);
+      var r = new Box2DVector(2);
       r[0] = new ClipVertex();
       r[1] = new ClipVertex();
       return r;
@@ -1424,7 +1424,7 @@ Box2D.postDefs = [];
       case b2Shape.e_circleShape:
          {
             var circle = (shape instanceof b2CircleShape ? shape : null);
-            this.m_vertices = new Vector(1, true);
+            this.m_vertices = new Box2DVector(1, true);
             this.m_vertices[0] = circle.m_p;
             this.m_count = 1;
             this.m_radius = circle.m_radius;
@@ -1534,7 +1534,7 @@ Box2D.postDefs = [];
    }
    b2DynamicTree.prototype.Query = function (callback, aabb) {
       if (this.m_root == null) return;
-      var stack = new Vector();
+      var stack = new Box2DVector();
       var count = 0;
       stack[count++] = this.m_root;
       while (count > 0) {
@@ -1570,7 +1570,7 @@ Box2D.postDefs = [];
          segmentAABB.upperBound.x = Math.max(p1.x, tX);
          segmentAABB.upperBound.y = Math.max(p1.y, tY);
       }
-      var stack = new Vector();
+      var stack = new Box2DVector();
       var count = 0;
       stack[count++] = this.m_root;
       while (count > 0) {
@@ -1713,8 +1713,8 @@ Box2D.postDefs = [];
    }
    b2DynamicTreeBroadPhase.b2DynamicTreeBroadPhase = function () {
       this.m_tree = new b2DynamicTree();
-      this.m_moveBuffer = new Vector();
-      this.m_pairBuffer = new Vector();
+      this.m_moveBuffer = new Box2DVector();
+      this.m_pairBuffer = new Box2DVector();
       this.m_pairCount = 0;
    };
    b2DynamicTreeBroadPhase.prototype.CreateProxy = function (aabb, userData) {
@@ -1819,7 +1819,7 @@ Box2D.postDefs = [];
       this.m_pointCount = 0;
    };
    b2Manifold.prototype.b2Manifold = function () {
-      this.m_points = new Vector(b2Settings.b2_maxManifoldPoints);
+      this.m_points = new Box2DVector(b2Settings.b2_maxManifoldPoints);
       for (var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) {
          this.m_points[i] = new b2ManifoldPoint();
       }
@@ -2177,7 +2177,7 @@ Box2D.postDefs = [];
       this.m_v1 = new b2SimplexVertex();
       this.m_v2 = new b2SimplexVertex();
       this.m_v3 = new b2SimplexVertex();
-      this.m_vertices = new Vector(3);
+      this.m_vertices = new Box2DVector(3);
    };
    b2Simplex.prototype.b2Simplex = function () {
       this.m_vertices[0] = this.m_v1;
@@ -2535,7 +2535,7 @@ Box2D.postDefs = [];
       this.m_normal = new b2Vec2();
    };
    b2WorldManifold.prototype.b2WorldManifold = function () {
-      this.m_points = new Vector(b2Settings.b2_maxManifoldPoints);
+      this.m_points = new Box2DVector(b2Settings.b2_maxManifoldPoints);
       for (var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) {
          this.m_points[i] = new b2Vec2();
       }
@@ -3099,7 +3099,7 @@ Box2D.postDefs = [];
    }
    b2PolygonShape.prototype.SetAsArray = function (vertices, vertexCount) {
       if (vertexCount === undefined) vertexCount = 0;
-      var v = new Vector();
+      var v = new Box2DVector();
       var i = 0,
          tVec;
       for (i = 0;
@@ -3468,8 +3468,8 @@ Box2D.postDefs = [];
       this.__super.b2Shape.call(this);
       this.m_type = b2Shape.e_polygonShape;
       this.m_centroid = new b2Vec2();
-      this.m_vertices = new Vector();
-      this.m_normals = new Vector();
+      this.m_vertices = new Box2DVector();
+      this.m_normals = new Box2DVector();
    }
    b2PolygonShape.prototype.Reserve = function (count) {
       if (count === undefined) count = 0;
@@ -3504,7 +3504,7 @@ Box2D.postDefs = [];
    b2PolygonShape.ComputeOBB = function (obb, vs, count) {
       if (count === undefined) count = 0;
       var i = 0;
-      var p = new Vector(count + 1);
+      var p = new Box2DVector(count + 1);
       for (i = 0;
       i < count; ++i) {
          p[i] = vs[i];
@@ -5450,9 +5450,9 @@ Box2D.postDefs = [];
    }
    b2Island.b2Island = function () {};
    b2Island.prototype.b2Island = function () {
-      this.m_bodies = new Vector();
-      this.m_contacts = new Vector();
-      this.m_joints = new Vector();
+      this.m_bodies = new Box2DVector();
+      this.m_contacts = new Box2DVector();
+      this.m_joints = new Box2DVector();
    }
    b2Island.prototype.Initialize = function (bodyCapacity, contactCapacity, jointCapacity, allocator, listener, contactSolver) {
       if (bodyCapacity === undefined) bodyCapacity = 0;
@@ -5691,7 +5691,7 @@ Box2D.postDefs = [];
       this.warmStarting = step.warmStarting;
    }
    b2World.b2World = function () {
-      this.s_stack = new Vector();
+      this.s_stack = new Box2DVector();
       this.m_contactManager = new b2ContactManager();
       this.m_contactSolver = new b2ContactSolver();
       this.m_island = new b2Island();
@@ -6156,7 +6156,7 @@ Box2D.postDefs = [];
    }
    b2World.prototype.RayCastAll = function (point1, point2) {
       var __this = this;
-      var result = new Vector();
+      var result = new Box2DVector();
 
       function RayCastAllWrapper(fixture, point, normal, fraction) {
          if (fraction === undefined) fraction = 0;
@@ -6521,7 +6521,7 @@ Box2D.postDefs = [];
             var poly = ((shape instanceof b2PolygonShape ? shape : null));
             var vertexCount = parseInt(poly.GetVertexCount());
             var localVertices = poly.GetVertices();
-            var vertices = new Vector(vertexCount);
+            var vertices = new Box2DVector(vertexCount);
             for (i = 0;
             i < vertexCount; ++i) {
                vertices[i] = b2Math.MulX(xf, localVertices[i]);
@@ -6543,7 +6543,7 @@ Box2D.postDefs = [];
       Box2D.Dynamics.b2World.s_backupA = new b2Sweep();
       Box2D.Dynamics.b2World.s_backupB = new b2Sweep();
       Box2D.Dynamics.b2World.s_timestep = new b2TimeStep();
-      Box2D.Dynamics.b2World.s_queue = new Vector();
+      Box2D.Dynamics.b2World.s_queue = new Box2DVector();
       Box2D.Dynamics.b2World.s_jointColor = new b2Color(0.5, 0.8, 0.8);
       Box2D.Dynamics.b2World.e_newFixture = 0x0001;
       Box2D.Dynamics.b2World.e_locked = 0x0002;
@@ -6827,7 +6827,7 @@ Box2D.postDefs = [];
       this.K = new b2Mat22();
    };
    b2ContactConstraint.prototype.b2ContactConstraint = function () {
-      this.points = new Vector(b2Settings.b2_maxManifoldPoints);
+      this.points = new Box2DVector(b2Settings.b2_maxManifoldPoints);
       for (var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) {
          this.points[i] = new b2ContactConstraintPoint();
       }
@@ -6856,9 +6856,9 @@ Box2D.postDefs = [];
       }
    }
    b2ContactFactory.prototype.InitializeRegisters = function () {
-      this.m_registers = new Vector(b2Shape.e_shapeTypeCount);
+      this.m_registers = new Box2DVector(b2Shape.e_shapeTypeCount);
       for (var i = 0; i < b2Shape.e_shapeTypeCount; i++) {
-         this.m_registers[i] = new Vector(b2Shape.e_shapeTypeCount);
+         this.m_registers[i] = new Box2DVector(b2Shape.e_shapeTypeCount);
          for (var j = 0; j < b2Shape.e_shapeTypeCount; j++) {
             this.m_registers[i][j] = new b2ContactRegister();
          }
@@ -6922,7 +6922,7 @@ Box2D.postDefs = [];
    };
    b2ContactSolver.b2ContactSolver = function () {
       this.m_step = new b2TimeStep();
-      this.m_constraints = new Vector();
+      this.m_constraints = new Box2DVector();
    };
    b2ContactSolver.prototype.b2ContactSolver = function () {}
    b2ContactSolver.prototype.Initialize = function (step, contacts, contactCount, allocator) {
@@ -7414,7 +7414,7 @@ Box2D.postDefs = [];
    b2PositionSolverManifold.prototype.b2PositionSolverManifold = function () {
       this.m_normal = new b2Vec2();
       this.m_separations = new Vector_a2j_Number(b2Settings.b2_maxManifoldPoints);
-      this.m_points = new Vector(b2Settings.b2_maxManifoldPoints);
+      this.m_points = new Box2DVector(b2Settings.b2_maxManifoldPoints);
       for (var i = 0; i < b2Settings.b2_maxManifoldPoints; i++) {
          this.m_points[i] = new b2Vec2();
       }