diff --git a/app/locale/en.coffee b/app/locale/en.coffee
index 6d0c82186..ec04add0f 100644
--- a/app/locale/en.coffee
+++ b/app/locale/en.coffee
@@ -274,6 +274,7 @@
     editor_config_indentguides_description: "Displays vertical lines to see indentation better."
     editor_config_behaviors_label: "Smart Behaviors"
     editor_config_behaviors_description: "Autocompletes brackets, braces, and quotes."
+    keyboard_shortcuts: "Key Shortcuts"
     loading_ready: "Ready!"
     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."
@@ -310,6 +311,23 @@
     infinite_loop_reset_level: "Reset Level"
     infinite_loop_comment_out: "Comment Out My Code"
 
+  keyboard_shortcuts:
+    keyboard_shortcuts: "Keyboard Shortcuts"
+    space: "Space"
+    enter: "Enter"
+    escape: "Escape"
+    cast_spell: "Cast current spell."
+    continue_script: "Continue past current script."
+    skip_scripts: "Skip past all skippable scripts."
+    toggle_playback: "Toggle play/pause."
+    scrub_playback: "Scrub back and forward through time."
+    scrub_execution: "Scrub through current spell execution."
+    toggle_debug: "Toggle debug display."
+    toggle_grid: "Toggle grid overlay."
+    toggle_pathfinding: "Toggle pathfinding overlay."
+    beautify: "Beautify your code by standardizing its formatting."
+    move_wizard: "Move your Wizard around the level."
+
   admin:
     av_title: "Admin Views"
     av_entities_sub_title: "Entities"
diff --git a/app/styles/play/level/modal/keyboard_shortcuts.sass b/app/styles/play/level/modal/keyboard_shortcuts.sass
new file mode 100644
index 000000000..f0b13b5f3
--- /dev/null
+++ b/app/styles/play/level/modal/keyboard_shortcuts.sass
@@ -0,0 +1,12 @@
+#keyboard-shortcuts-modal
+  dl.dl-horizontal
+    dt
+      width: 120px
+
+      code
+        color: #333
+        font-family: "Helvetica Neue", Helvetica, Arial, sans-serif
+        font-size: 100%
+
+    dd
+      margin-left: 140px
diff --git a/app/templates/play/level/modal/keyboard_shortcuts.jade b/app/templates/play/level/modal/keyboard_shortcuts.jade
new file mode 100644
index 000000000..195558eed
--- /dev/null
+++ b/app/templates/play/level/modal/keyboard_shortcuts.jade
@@ -0,0 +1,63 @@
+extends /templates/modal/modal_base
+
+block modal-header-content
+  h3(data-i18n="keyboard_shortcuts.keyboard_shortcuts") Keyboard Shortcuts
+
+block modal-body-content
+  dl.dl-horizontal
+    dt(title="Shift+" + enter)
+      code ⇧+#{enter}
+    dd(data-i18n="keyboard_shortcuts.cast_spell") Cast current spell.
+  dl.dl-horizontal
+    dt(title="Shift+" + space)
+      code ⇧+#{space}
+    dd(data-i18n="keyboard_shortcuts.continue_script") Continue past current script.
+  dl.dl-horizontal
+    dt(title=escapeKey)
+      code Esc
+    dd(data-i18n="keyboard_shortcuts.skip_scripts") Skip past all skippable scripts.
+  dl.dl-horizontal
+    dt(title=ctrlName + "+P")
+      code #{ctrl}+P
+    dd(data-i18n="keyboard_shortcuts.toggle_playback") Toggle play/pause.
+  dl.dl-horizontal
+    dt(title=ctrlName + "+[, " + ctrlName + "+]")
+      code #{ctrl}+[
+      | , 
+      code #{ctrl}+]
+    dd(data-i18n="keyboard_shortcuts.scrub_playback") Scrub back and forward through time.
+  dl.dl-horizontal
+    dt(title=ctrlName + "+" + altName + "+[, " + ctrlName + "+" + altName + "+]")
+      code #{ctrl}+#{alt}+[
+      | , 
+      code #{ctrl}+#{alt}+]
+    dd(data-i18n="keyboard_shortcuts.scrub_execution") Scrub through current spell execution.
+  dl.dl-horizontal
+    dt(title=ctrlName + "+\\")
+      code #{ctrl}+\
+    dd(data-i18n="keyboard_shortcuts.toggle_debug") Toggle debug display.
+  dl.dl-horizontal
+    dt(title=ctrlName + "+G")
+      code #{ctrl}+G
+    dd(data-i18n="keyboard_shortcuts.toggle_grid") Toggle grid overlay.
+  dl.dl-horizontal
+    dt(title=ctrlName + "+O")
+      code #{ctrl}+O
+    dd(data-i18n="keyboard_shortcuts.toggle_pathfinding") Toggle pathfinding overlay.
+  dl.dl-horizontal
+    dt(title=ctrlName + "+Shift+B")
+      code #{ctrl}+⇧+B
+    dd(data-i18n="keyboard_shortcuts.beautify") Beautify your code by standardizing its formatting.
+  dl.dl-horizontal
+    dt(title="Arrow keys")
+      code ←
+      | , 
+      code →
+      | , 
+      code ↑
+      | , 
+      code ↓
+    dd(data-i18n="keyboard_shortcuts.move_wizard") Move your Wizard around the level.
+
+block modal-footer-content
+  a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="modal.close").btn.btn-primary Close
diff --git a/app/templates/play/level/playback.jade b/app/templates/play/level/playback.jade
index 6406aa71b..afcbd8276 100644
--- a/app/templates/play/level/playback.jade
+++ b/app/templates/play/level/playback.jade
@@ -34,6 +34,9 @@ button.btn.btn-xs.btn-inverse#music-button(title="Toggle Music")
         i.icon-globe
         | Debug Mode
         i.icon-ok.secret
+    li.selectable#view-keyboard-shortcuts
+      i.icon-info-sign
+      span(data-i18n="play_level.keyboard_shortcuts") Key Shortcuts
     li(title="Ctrl/Cmd + G: Toggle grid display").selectable#grid-toggle
       i.icon-th
       span(data-i18n="play_level.grid") Grid
diff --git a/app/views/kinds/CocoView.coffee b/app/views/kinds/CocoView.coffee
index aea3dfcf2..3e2f2a9b7 100644
--- a/app/views/kinds/CocoView.coffee
+++ b/app/views/kinds/CocoView.coffee
@@ -38,7 +38,7 @@ module.exports = class CocoView extends Backbone.View
       @supermodel.models = options.supermodel.models
       @supermodel.collections = options.supermodel.collections
       @supermodel.shouldSaveBackups = options.supermodel.shouldSaveBackups
-      
+
     @subscriptions = utils.combineAncestralObject(@, 'subscriptions')
     @events = utils.combineAncestralObject(@, 'events')
     @scope = makeScopeName()
@@ -118,7 +118,7 @@ module.exports = class CocoView extends Backbone.View
   updateProgress: (progress) ->
     @loadProgress.progress = progress if progress > @loadProgress.progress
     @updateProgressBar(progress)
-      
+
   updateProgressBar: (progress) =>
     prog = "#{parseInt(progress*100)}%"
     @$el?.find('.loading-container .progress-bar').css('width', prog)
@@ -134,11 +134,11 @@ module.exports = class CocoView extends Backbone.View
       resourceIndex: r.rid,
       responseText: r.jqxhr?.responseText
     })).i18n()
-  
+
   onRetryResource: (e) ->
     res = @supermodel.getResource($(e.target).data('resource-index'))
     # different views may respond to this call, and not all have the resource to reload
-    return unless res and res.isFailed 
+    return unless res and res.isFailed
     res.load()
     $(e.target).closest('.loading-error-alert').remove()
 
@@ -278,6 +278,9 @@ module.exports = class CocoView extends Backbone.View
     ua = navigator.userAgent or navigator.vendor or window.opera
     return ua.search("MSIE") != -1
 
+  isMac: ->
+    navigator.platform.toUpperCase().indexOf('MAC') isnt -1
+
   initSlider: ($el, startValue, changeCallback) ->
     slider = $el.slider({ animate: "fast" })
     slider.slider('value', startValue)
diff --git a/app/views/play/level/modal/keyboard_shortcuts_modal.coffee b/app/views/play/level/modal/keyboard_shortcuts_modal.coffee
new file mode 100644
index 000000000..816bd3f10
--- /dev/null
+++ b/app/views/play/level/modal/keyboard_shortcuts_modal.coffee
@@ -0,0 +1,17 @@
+View = require 'views/kinds/ModalView'
+template = require 'templates/play/level/modal/keyboard_shortcuts'
+
+module.exports = class KeyboardShortcutsModal extends View
+  id: 'keyboard-shortcuts-modal'
+  template: template
+
+  getRenderData: ->
+    c = super()
+    c.ctrl = if @isMac() then '⌘' else '^'
+    c.ctrlName = if @isMac() then 'Cmd' else 'Ctrl'
+    c.alt = if @isMac() then '⌥' else 'alt'
+    c.altName = if @isMac() then 'Opt' else 'Alt'
+    c.enter = $.i18n.t 'keyboard_shortcuts.enter'
+    c.space = $.i18n.t 'keyboard_shortcuts.space'
+    c.escapeKey = $.i18n.t 'keyboard_shortcuts.escape'
+    c
diff --git a/app/views/play/level/playback_view.coffee b/app/views/play/level/playback_view.coffee
index 5b72f03ee..8bc581e1e 100644
--- a/app/views/play/level/playback_view.coffee
+++ b/app/views/play/level/playback_view.coffee
@@ -3,6 +3,7 @@ template = require 'templates/play/level/playback'
 {me} = require 'lib/auth'
 
 EditorConfigModal = require './modal/editor_config_modal'
+KeyboardShortcutsModal = require './modal/keyboard_shortcuts_modal'
 
 module.exports = class PlaybackView extends View
   id: "playback-view"
@@ -29,6 +30,7 @@ module.exports = class PlaybackView extends View
     'click #grid-toggle': 'onToggleGrid'
     'click #edit-wizard-settings': 'onEditWizardSettings'
     'click #edit-editor-config': 'onEditEditorConfig'
+    'click #view-keyboard-shortcuts': 'onViewKeyboardShortcuts'
     'click #music-button': 'onToggleMusic'
     'click #zoom-in-button': -> Backbone.Mediator.publish('camera-zoom-in') unless @shouldIgnore()
     'click #zoom-out-button': -> Backbone.Mediator.publish('camera-zoom-out') unless @shouldIgnore()
@@ -181,6 +183,9 @@ module.exports = class PlaybackView extends View
   onEditEditorConfig: ->
     @openModalView new EditorConfigModal session: @options.session
 
+  onViewKeyboardShortcuts: ->
+    @openModalView new KeyboardShortcutsModal()
+
   onCastSpells: (e) ->
     return if e.preload
     @casting = true
diff --git a/app/views/play/level/tome/cast_button_view.coffee b/app/views/play/level/tome/cast_button_view.coffee
index bd06e8019..9bea863f2 100644
--- a/app/views/play/level/tome/cast_button_view.coffee
+++ b/app/views/play/level/tome/cast_button_view.coffee
@@ -20,7 +20,6 @@ module.exports = class CastButtonView extends View
     super options
     @spells = options.spells
     @levelID = options.levelID
-    isMac = navigator.platform.toUpperCase().indexOf('MAC') isnt -1
     @castShortcut = "⇧↵"
     @castShortcutVerbose = "Shift+Enter"
 
@@ -79,7 +78,7 @@ module.exports = class CastButtonView extends View
     async.some _.values(@spells), (spell, callback) =>
       spell.hasChangedSignificantly spell.getSource(), null, callback
     , (castable) =>
-      Backbone.Mediator.publish 'tome:spell-has-changed-significantly-calculation', hasChangedSignificantly: castable 
+      Backbone.Mediator.publish 'tome:spell-has-changed-significantly-calculation', hasChangedSignificantly: castable
       @castButtonGroup.toggleClass('castable', castable).toggleClass('casting', @casting)
       if @casting
         s = $.i18n.t("play_level.tome_cast_button_casting", defaultValue: "Casting")