diff --git a/src/engine/blocks.js b/src/engine/blocks.js
new file mode 100644
index 000000000..26351c1b1
--- /dev/null
+++ b/src/engine/blocks.js
@@ -0,0 +1,199 @@
+/**
+* @fileoverview
+ * Store and mutate the VM block representation,
+ * and handle updates from Scratch Blocks events.
+ */
+
+ function Blocks () {
+     /**
+      * All blocks in the workspace.
+      * Keys are block IDs, values are metadata about the block.
+      * @type {Object.<string, Object>}
+      */
+     this._blocks = {};
+
+     /**
+      * All stacks in the workspace.
+      * A list of block IDs that represent stacks (first block in stack).
+      * @type {Array.<String>}
+      */
+     this._stacks = [];
+ }
+
+/**
+ * Provide an object with metadata for the requested block ID.
+ * @param {!string} blockId ID of block we have stored.
+ * @return {?Object} Metadata about the block, if it exists.
+ */
+ Blocks.prototype.getBlock = function (blockId) {
+     return this._blocks[blockId];
+ };
+
+/**
+ * Get all known top-level blocks that start stacks.
+ * @return {Array.<string>} List of block IDs.
+ */
+ Blocks.prototype.getStacks = function () {
+     return this._stacks;
+ };
+
+ /**
+  * Get the next block for a particular block
+  * @param {?string} id ID of block to get the next block for
+  * @return {?string} ID of next block in the sequence
+  */
+ Blocks.prototype.getNextBlock = function (id) {
+     if (typeof this._blocks[id] === 'undefined') return null;
+     return this._blocks[id].next;
+ };
+
+ /**
+  * Get the substack for a particular C-shaped block
+  * @param {?string} id ID for block to get the substack for
+  * @return {?string} ID of block in the substack
+  */
+ Blocks.prototype.getSubstack = function (id) {
+     var block = this._blocks[id];
+     if (typeof block === 'undefined') return null;
+     // Empty C-block?
+     if (!('SUBSTACK' in block.inputs)) return null;
+     return block.inputs['SUBSTACK'].block;
+ };
+
+ /**
+  * Get the opcode for a particular block
+  * @param {?string} id ID of block to query
+  * @return {?string} the opcode corresponding to that block
+  */
+ Blocks.prototype.getOpcode = function (id) {
+     if (typeof this._blocks[id] === 'undefined') return null;
+     return this._blocks[id].opcode;
+ };
+
+ // ---------------------------------------------------------------------
+
+ /**
+  * Block management: create blocks and stacks from a `create` event
+  * @param {!Object} block Blockly create event to be processed
+  */
+ Blocks.prototype.createBlock = function (block, opt_isFlyoutBlock) {
+     // Create new block
+     this._blocks[block.id] = block;
+
+     // Push block id to stacks array.
+     // Blocks are added as a top-level stack if they are marked as a topBlock
+     // (if they were top-level XML in the event) and if they are not
+     // flyout blocks.
+     if (!opt_isFlyoutBlock && block.topBlock) {
+         this._addStack(block.id);
+     }
+ };
+
+ /**
+  * Block management: change block field values
+  * @param {!Object} args Blockly change event to be processed
+  */
+ Blocks.prototype.changeBlock = function (args) {
+     // Validate
+     if (args.element !== 'field') return;
+     if (typeof this._blocks[args.id] === 'undefined') return;
+     if (typeof this._blocks[args.id].fields[args.name] === 'undefined') return;
+
+     // Update block value
+     this._blocks[args.id].fields[args.name].value = args.value;
+ };
+
+ /**
+  * Block management: move blocks from parent to parent
+  * @param {!Object} e Blockly move event to be processed
+  */
+ Blocks.prototype.moveBlock = function (e) {
+     // Remove from any old parent.
+     if (e.oldParent !== undefined) {
+         var oldParent = this._blocks[e.oldParent];
+         if (e.oldInput !== undefined &&
+             oldParent.inputs[e.oldInput].block === e.id) {
+             // This block was connected to the old parent's input.
+             oldParent.inputs[e.oldInput].block = null;
+         } else if (oldParent.next === e.id) {
+             // This block was connected to the old parent's next connection.
+             oldParent.next = null;
+         }
+     }
+
+     // Has the block become a top-level block?
+     if (e.newParent === undefined) {
+         this._addStack(e.id);
+     } else {
+         // Remove stack, if one exists.
+         this._deleteStack(e.id);
+         // Otherwise, try to connect it in its new place.
+         if (e.newInput !== undefined) {
+             // Moved to the new parent's input.
+             this._blocks[e.newParent].inputs[e.newInput] = {
+                 name: e.newInput,
+                 block: e.id
+             };
+         } else {
+             // Moved to the new parent's next connection.
+             this._blocks[e.newParent].next = e.id;
+         }
+     }
+ };
+
+ /**
+  * Block management: delete blocks and their associated stacks
+  * @param {!Object} e Blockly delete event to be processed
+  */
+ Blocks.prototype.deleteBlock = function (e) {
+     // @todo In runtime, stop threads running on this stack
+
+     // Get block
+     var block = this._blocks[e.id];
+
+     // Delete children
+     if (block.next !== null) {
+         this.deleteBlock({id: block.next});
+     }
+
+     // Delete inputs (including substacks)
+     for (var input in block.inputs) {
+         // If it's null, the block in this input moved away.
+         if (block.inputs[input].block !== null) {
+             this.deleteBlock({id: block.inputs[input].block});
+         }
+     }
+
+     // Delete stack
+     this._deleteStack(e.id);
+
+     // Delete block
+     delete this._blocks[e.id];
+ };
+
+ // ---------------------------------------------------------------------
+
+ /**
+  * Helper to add a stack to `this._stacks`
+  * @param {?string} id ID of block that starts the stack
+  */
+ Blocks.prototype._addStack = function (id) {
+     var i = this._stacks.indexOf(id);
+     if (i > -1) return; // Already in stacks.
+     this._stacks.push(id);
+     // Update `topLevel` property on the top block.
+     this._blocks[id].topLevel = true;
+ };
+
+ /**
+  * Helper to remove a stack from `this._stacks`
+  * @param {?string} id ID of block that starts the stack
+  */
+ Blocks.prototype._deleteStack = function (id) {
+     var i = this._stacks.indexOf(id);
+     if (i > -1) this._stacks.splice(i, 1);
+     // Update `topLevel` property on the top block.
+     if (this._blocks[id]) this._blocks[id].topLevel = false;
+ };
+
+ module.exports = Blocks;
diff --git a/src/engine/runtime.js b/src/engine/runtime.js
index 17013576c..9531eadc7 100644
--- a/src/engine/runtime.js
+++ b/src/engine/runtime.js
@@ -10,25 +10,18 @@ var defaultBlockPackages = {
 
 /**
  * Manages blocks, stacks, and the sequencer.
+ * @param blocks Blocks instance for this runtime.
  */
-function Runtime () {
+function Runtime (blocks) {
     // Bind event emitter
     EventEmitter.call(this);
 
     // State for the runtime
-    /**
-     * All blocks in the workspace.
-     * Keys are block IDs, values are metadata about the block.
-     * @type {Object.<string, Object>}
-     */
-    this.blocks = {};
 
     /**
-     * All stacks in the workspace.
-     * A list of block IDs that represent stacks (first block in stack).
-     * @type {Array.<String>}
+     * Block management and storage
      */
-    this.stacks = [];
+    this.blocks = blocks;
 
     /**
      * A list of threads that are currently running in the VM.
@@ -83,106 +76,6 @@ util.inherits(Runtime, EventEmitter);
  */
 Runtime.THREAD_STEP_INTERVAL = 1000 / 30;
 
-/**
- * Block management: create blocks and stacks from a `create` event
- * @param {!Object} block Blockly create event to be processed
- */
-Runtime.prototype.createBlock = function (block, opt_isFlyoutBlock) {
-    // Create new block
-    this.blocks[block.id] = block;
-
-    // Push block id to stacks array.
-    // Blocks are added as a top-level stack if they are marked as a topBlock
-    // (if they were top-level XML in the event) and if they are not
-    // flyout blocks.
-    if (!opt_isFlyoutBlock && block.topBlock) {
-        this._addStack(block.id);
-    }
-};
-
-/**
- * Block management: change block field values
- * @param {!Object} args Blockly change event to be processed
- */
-Runtime.prototype.changeBlock = function (args) {
-    // Validate
-    if (args.element !== 'field') return;
-    if (typeof this.blocks[args.id] === 'undefined') return;
-    if (typeof this.blocks[args.id].fields[args.name] === 'undefined') return;
-
-    // Update block value
-    this.blocks[args.id].fields[args.name].value = args.value;
-};
-
-/**
- * Block management: move blocks from parent to parent
- * @param {!Object} e Blockly move event to be processed
- */
-Runtime.prototype.moveBlock = function (e) {
-    var _this = this;
-
-    // Remove from any old parent.
-    if (e.oldParent !== undefined) {
-        var oldParent = _this.blocks[e.oldParent];
-        if (e.oldInput !== undefined &&
-            oldParent.inputs[e.oldInput].block === e.id) {
-            // This block was connected to the old parent's input.
-            oldParent.inputs[e.oldInput].block = null;
-        } else if (oldParent.next === e.id) {
-            // This block was connected to the old parent's next connection.
-            oldParent.next = null;
-        }
-    }
-
-    // Has the block become a top-level block?
-    if (e.newParent === undefined) {
-        _this._addStack(e.id);
-    } else {
-        // Remove stack, if one exists.
-        _this._deleteStack(e.id);
-        // Otherwise, try to connect it in its new place.
-        if (e.newInput !== undefined) {
-            // Moved to the new parent's input.
-            _this.blocks[e.newParent].inputs[e.newInput] = {
-                name: e.newInput,
-                block: e.id
-            };
-        } else {
-            // Moved to the new parent's next connection.
-            _this.blocks[e.newParent].next = e.id;
-        }
-    }
-};
-
-/**
- * Block management: delete blocks and their associated stacks
- * @param {!Object} e Blockly delete event to be processed
- */
-Runtime.prototype.deleteBlock = function (e) {
-    // @todo Stop threads running on this stack
-
-    // Get block
-    var block = this.blocks[e.id];
-
-    // Delete children
-    if (block.next !== null) {
-        this.deleteBlock({id: block.next});
-    }
-
-    // Delete inputs (including substacks)
-    for (var input in block.inputs) {
-        // If it's null, the block in this input moved away.
-        if (block.inputs[input].block !== null) {
-            this.deleteBlock({id: block.inputs[input].block});
-        }
-    }
-
-    // Delete stack
-    this._deleteStack(e.id);
-
-    // Delete block
-    delete this.blocks[e.id];
-};
 
 // -----------------------------------------------------------------------------
 // -----------------------------------------------------------------------------
@@ -268,10 +161,11 @@ Runtime.prototype.greenFlag = function () {
         this._removeThread(this.threads[i]);
     }
     // Add all top stacks with green flag
-    for (var j = 0; j < this.stacks.length; j++) {
-        var topBlock = this.stacks[j];
-        if (this.blocks[topBlock].opcode === 'event_whenflagclicked') {
-            this._pushThread(this.stacks[j]);
+    var stacks = this.blocks.getStacks();
+    for (var j = 0; j < stacks.length; j++) {
+        var topBlock = stacks[j];
+        if (this.blocks.getBlock(topBlock).opcode === 'event_whenflagclicked') {
+            this._pushThread(stacks[j]);
         }
     }
 };
@@ -281,9 +175,11 @@ Runtime.prototype.greenFlag = function () {
  */
 Runtime.prototype.startDistanceSensors = function () {
     // Add all top stacks with distance sensor
-    for (var j = 0; j < this.stacks.length; j++) {
-        var topBlock = this.stacks[j];
-        if (this.blocks[topBlock].opcode === 'wedo_whendistanceclose') {
+    var stacks = this.blocks.getStacks();
+    for (var j = 0; j < stacks.length; j++) {
+        var topBlock = stacks[j];
+        if (this.blocks.getBlock(topBlock).opcode ===
+            'wedo_whendistanceclose') {
             var alreadyRunning = false;
             for (var k = 0; k < this.threads.length; k++) {
                 if (this.threads[k].topBlock === topBlock) {
@@ -291,7 +187,7 @@ Runtime.prototype.startDistanceSensors = function () {
                 }
             }
             if (!alreadyRunning) {
-                this._pushThread(this.stacks[j]);
+                this._pushThread(stacks[j]);
             }
         }
     }
@@ -345,60 +241,4 @@ Runtime.prototype.start = function () {
     }.bind(this), Runtime.THREAD_STEP_INTERVAL);
 };
 
-// -----------------------------------------------------------------------------
-// -----------------------------------------------------------------------------
-
-/**
- * Helper to add a stack to `this.stacks`
- * @param {?string} id ID of block that starts the stack
- */
-Runtime.prototype._addStack = function (id) {
-    var i = this.stacks.indexOf(id);
-    if (i > -1) return; // Already in stacks.
-    this.stacks.push(id);
-    // Update `topLevel` property on the top block.
-    this.blocks[id].topLevel = true;
-};
-
-/**
- * Helper to remove a stack from `this.stacks`
- * @param {?string} id ID of block that starts the stack
- */
-Runtime.prototype._deleteStack = function (id) {
-    var i = this.stacks.indexOf(id);
-    if (i > -1) this.stacks.splice(i, 1);
-    // Update `topLevel` property on the top block.
-    if (this.blocks[id]) this.blocks[id].topLevel = false;
-};
-
-/**
- * Helper to get the next block for a particular block
- * @param {?string} id ID of block to get the next block for
- * @return {?string} ID of next block in the sequence
- */
-Runtime.prototype._getNextBlock = function (id) {
-    if (typeof this.blocks[id] === 'undefined') return null;
-    return this.blocks[id].next;
-};
-
-/**
- * Helper to get the substack for a particular C-shaped block
- * @param {?string} id ID for block to get the substack for
- * @return {?string} ID of block in the substack
- */
-Runtime.prototype._getSubstack = function (id) {
-    if (typeof this.blocks[id] === 'undefined') return null;
-    return this.blocks[id].fields['SUBSTACK'];
-};
-
-/**
- * Helper to get the opcode for a particular block
- * @param {?string} id ID of block to query
- * @return {?string} the opcode corresponding to that block
- */
-Runtime.prototype._getOpcode = function (id) {
-    if (typeof this.blocks[id] === 'undefined') return null;
-    return this.blocks[id].opcode;
-};
-
 module.exports = Runtime;
diff --git a/src/engine/sequencer.js b/src/engine/sequencer.js
index f495264c8..95a28f7bf 100644
--- a/src/engine/sequencer.js
+++ b/src/engine/sequencer.js
@@ -97,13 +97,13 @@ Sequencer.prototype.stepThread = function (thread) {
     // If the primitive would like to do control flow,
     // it can overwrite nextBlock.
     var currentBlock = thread.nextBlock;
-    if (!currentBlock || !this.runtime.blocks[currentBlock]) {
+    if (!currentBlock || !this.runtime.blocks.getBlock(currentBlock)) {
         thread.status = Thread.STATUS_DONE;
         return;
     }
-    thread.nextBlock = this.runtime._getNextBlock(currentBlock);
+    thread.nextBlock = this.runtime.blocks.getNextBlock(currentBlock);
 
-    var opcode = this.runtime._getOpcode(currentBlock);
+    var opcode = this.runtime.blocks.getOpcode(currentBlock);
 
     // Push the current block to the stack
     thread.stack.push(currentBlock);
@@ -130,7 +130,7 @@ Sequencer.prototype.stepThread = function (thread) {
     var threadDoneCallback = function () {
         thread.status = Thread.STATUS_DONE;
         // Refresh nextBlock in case it has changed during a yield.
-        thread.nextBlock = instance.runtime._getNextBlock(currentBlock);
+        thread.nextBlock = instance.runtime.blocks.getNextBlock(currentBlock);
         // Pop the stack and stack frame
         thread.stack.pop();
         thread.stackFrames.pop();
@@ -141,9 +141,10 @@ Sequencer.prototype.stepThread = function (thread) {
      * @todo very hacked...
      */
     var startHats = function(callback) {
-        for (var i = 0; i < instance.runtime.stacks.length; i++) {
-            var stack = instance.runtime.stacks[i];
-            var stackBlock = instance.runtime.blocks[stack];
+        var stacks = instance.runtime.blocks.getStacks();
+        for (var i = 0; i < stacks.length; i++) {
+            var stack = stacks[i];
+            var stackBlock = instance.runtime.blocks.getBlock(stack);
             var result = callback(stackBlock);
             if (result) {
                 // Check if the stack is already running
@@ -174,7 +175,7 @@ Sequencer.prototype.stepThread = function (thread) {
      */
     var threadStartSubstack = function () {
         // Set nextBlock to the start of the substack
-        var substack = instance.runtime._getSubstack(currentBlock);
+        var substack = instance.runtime.blocks.getSubstack(currentBlock);
         if (substack && substack.value) {
             thread.nextBlock = substack.value;
         } else {
@@ -185,7 +186,7 @@ Sequencer.prototype.stepThread = function (thread) {
 
     // @todo extreme hack to get the single argument value for prototype
     var argValues = [];
-    var blockInputs = this.runtime.blocks[currentBlock].fields;
+    var blockInputs = this.runtime.blocks.getBlock(currentBlock).fields;
     for (var bi in blockInputs) {
         var outer = blockInputs[bi];
         for (var b in outer.blocks) {
diff --git a/src/index.js b/src/index.js
index f8e7e1ca4..ce147657c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,6 +1,7 @@
 var EventEmitter = require('events');
 var util = require('util');
 
+var Blocks = require('./engine/Blocks');
 var Runtime = require('./engine/runtime');
 var adapter = require('./engine/adapter');
 
@@ -15,7 +16,8 @@ function VirtualMachine () {
     // Bind event emitter and runtime to VM instance
     // @todo Post message (Web Worker) polyfill
     EventEmitter.call(instance);
-    instance.runtime = new Runtime();
+    instance.blocks = new Blocks();
+    instance.runtime = new Runtime(instance.blocks);
 
     /**
      * Event listener for blocks. Handles validation and serves as a generic
@@ -34,11 +36,11 @@ function VirtualMachine () {
             var newBlocks = adapter(e);
             // A create event can create many blocks. Add them all.
             for (var i = 0; i < newBlocks.length; i++) {
-                instance.runtime.createBlock(newBlocks[i], false);
+                instance.blocks.createBlock(newBlocks[i], false);
             }
             break;
         case 'change':
-            instance.runtime.changeBlock({
+            instance.blocks.changeBlock({
                 id: e.blockId,
                 element: e.element,
                 name: e.name,
@@ -46,7 +48,7 @@ function VirtualMachine () {
             });
             break;
         case 'move':
-            instance.runtime.moveBlock({
+            instance.blocks.moveBlock({
                 id: e.blockId,
                 oldParent: e.oldParentId,
                 oldInput: e.oldInputName,
@@ -55,7 +57,7 @@ function VirtualMachine () {
             });
             break;
         case 'delete':
-            instance.runtime.deleteBlock({
+            instance.blocks.deleteBlock({
                 id: e.blockId
             });
             break;