diff --git a/app/lib/surface/WizardSprite.coffee b/app/lib/surface/WizardSprite.coffee
index 9326521ba..4050fa6c0 100644
--- a/app/lib/surface/WizardSprite.coffee
+++ b/app/lib/surface/WizardSprite.coffee
@@ -28,7 +28,6 @@ module.exports = class WizardSprite extends IndieSprite
     super thangType, options
     @isSelf = options.isSelf
     @targetPos = @thang.pos
-    console.log "have @targetPos", @targetPos
     if @isSelf
       @setNameLabel me.displayName()
       @setColorHue me.get('wizardColor1')
diff --git a/app/styles/play/level/tome/problem_alert.sass b/app/styles/play/level/tome/problem_alert.sass
index 15a117e26..df410f680 100644
--- a/app/styles/play/level/tome/problem_alert.sass
+++ b/app/styles/play/level/tome/problem_alert.sass
@@ -23,6 +23,9 @@
 
     &:hover, &:focus
       @include opacity(1)
+
+  .problem-hint
+    font-size: 80%
       
   //&.alert-error
 
diff --git a/app/styles/play/level/tome/spell.sass b/app/styles/play/level/tome/spell.sass
index 588a4d43b..751ddea22 100644
--- a/app/styles/play/level/tome/spell.sass
+++ b/app/styles/play/level/tome/spell.sass
@@ -83,7 +83,7 @@
     .problem-marker-error
       background-color: rgba(110, 45, 27, 0.25)
 
-    .ace_gutter-cell.executing
+    .ace_gutter-cell.executing:not(.ace_error)
       margin-left: 1px
       background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMjgwMTE3NDA3MjA2ODExOEE2REU4Q0M1MTM1MkIxRiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpBQjVEQUNDMzQ4RUIxMUUxOEVGRUUyNzFENDM3RDVFMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpBQjVEQUNDMjQ4RUIxMUUxOEVGRUUyNzFENDM3RDVFMCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1LjEgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTU1MjE3RDIzMTIwNjgxMThEQkI4NTlBMjQ1QTEwOTUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDI4MDExNzQwNzIwNjgxMThBNkRFOENDNTEzNTJCMUYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7SazaGAAAAiElEQVR42mL8//8/AzUBEwOVweA3kAWboI2jCyhgDwBx4ZH9ey5Qy4UOQHweaHg/EAtQ08sFUIMDqBmGCkC8HmgoCCtQM1ICoK5toGYsg8KzHmjo+UGbDj8AcSMwORkSnQ7xgA3QtPmApISNBTyAGrSBGl6eAMSGxBhGyIVkZT3G0fKQYgAQYACL+C2ZM6PC7AAAAABJRU5ErkJggg==)
       background-position: 0px center
diff --git a/app/templates/play/level/tome/problem_alert.jade b/app/templates/play/level/tome/problem_alert.jade
index f3f374ee6..6b7e8383a 100644
--- a/app/templates/play/level/tome/problem_alert.jade
+++ b/app/templates/play/level/tome/problem_alert.jade
@@ -1,3 +1,5 @@
 button.close(type="button", data-dismiss="alert") ×
-span
-  != message
+span.problem-message= message
+if hint
+  br
+  span.problem-hint= hint
\ No newline at end of file
diff --git a/app/views/play/level/tome/problem.coffee b/app/views/play/level/tome/problem.coffee
index aeb011ad4..4549c137d 100644
--- a/app/views/play/level/tome/problem.coffee
+++ b/app/views/play/level/tome/problem.coffee
@@ -20,8 +20,8 @@ module.exports = class Problem
     text = @aetherProblem.message.replace /^Line \d+: /, ''
     start = @aetherProblem.ranges[0][0]
     @annotation =
-      row: start[0],
-      column: start[1],
+      row: start.row,
+      column: start.col,
       raw: text,
       text: text,
       type: @aetherProblem.level ? "error"
@@ -35,7 +35,7 @@ module.exports = class Problem
     return unless @aetherProblem.ranges
     [start, end] = @aetherProblem.ranges[0]
     clazz = "problem-marker-#{@aetherProblem.level}"
-    @markerRange = new Range(start[0], start[1], end[0], end[1])
+    @markerRange = new Range start.row, start.col, end.row, end.col
     @markerRange.start = @ace.getSession().getDocument().createAnchor @markerRange.start
     @markerRange.end = @ace.getSession().getDocument().createAnchor @markerRange.end
     @markerRange.id = @ace.getSession().addMarker @markerRange, clazz, "text"
diff --git a/app/views/play/level/tome/problem_alert_view.coffee b/app/views/play/level/tome/problem_alert_view.coffee
index 7559fb6e8..5c7ba5f5c 100644
--- a/app/views/play/level/tome/problem_alert_view.coffee
+++ b/app/views/play/level/tome/problem_alert_view.coffee
@@ -18,6 +18,7 @@ module.exports = class ProblemAlertView extends View
   getRenderData: (context={}) =>
     context = super context
     context.message = @problem.aetherProblem.message.replace("\n", "<br>")
+    context.hint = @problem.aetherProblem.hint?.replace("\n", "<br>")
     context
 
   afterRender: ->
diff --git a/app/views/play/level/tome/spell.coffee b/app/views/play/level/tome/spell.coffee
index 45592fa6a..17cc4d748 100644
--- a/app/views/play/level/tome/spell.coffee
+++ b/app/views/play/level/tome/spell.coffee
@@ -65,6 +65,7 @@ module.exports = class Spell
     aetherOptions =
       problems:
         jshint_W040: {level: "ignore"}
+        jshint_W030: {level: "ignore"}  # aether_NoEffect instead
         aether_MissingThis: {level: (if thang.requiresThis then 'error' else 'warning')}
       functionName: @name
       functionParameters: @parameters
diff --git a/app/views/play/level/tome/spell_view.coffee b/app/views/play/level/tome/spell_view.coffee
index 573d2096d..cd4235f34 100644
--- a/app/views/play/level/tome/spell_view.coffee
+++ b/app/views/play/level/tome/spell_view.coffee
@@ -288,10 +288,12 @@ module.exports = class SpellView extends View
     return unless aether = @spellThang?.aether
     source = @getSource()
     codeHasChangedSignificantly = force or @spell.hasChangedSignificantly source, aether.raw
-    return unless codeHasChangedSignificantly or @spellThang isnt @lastUpdatedAetherSpellThang
+    needsUpdate = codeHasChangedSignificantly or @spellThang isnt @lastUpdatedAetherSpellThang
+    return if not needsUpdate and aether is @displayedAether
     castAether = @spellThang.castAether
     codeIsAsCast = castAether and not @spell.hasChangedSignificantly source, castAether.raw
     aether = castAether if codeIsAsCast
+    return if not needsUpdate and aether is @displayedAether
 
     # Now that that's figured out, perform the update.
     @clearAetherDisplay()
@@ -307,6 +309,7 @@ module.exports = class SpellView extends View
     @highlightCurrentLine {}  # This'll remove all highlights
 
   displayAether: (aether) ->
+    @displayedAether = aether
     isCast = not _.isEmpty(aether.metrics) or _.some aether.problems.errors, {type: 'runtime'}
     @problems = []
     annotations = []
@@ -379,7 +382,7 @@ module.exports = class SpellView extends View
       #console.log thangID, "got new castAether with raw", aether.raw, "problems", aether.problems
       spellThang.castAether = aether
       spellThang.aether = @spell.createAether e.world.getThangByID(thangID)
-      console.log thangID, @spell.spellKey, "ran", aether.metrics.callsExecuted, "times over", aether.metrics.statementsExecuted, "statements, with max recursion depth", aether.metrics.maxDepth, "and full flow/metrics", aether.metrics, aether.flow
+      #console.log thangID, @spell.spellKey, "ran", aether.metrics.callsExecuted, "times over", aether.metrics.statementsExecuted, "statements, with max recursion depth", aether.metrics.maxDepth, "and full flow/metrics", aether.metrics, aether.flow
     @spell.transpile()
     @updateAether false, false
 
@@ -427,12 +430,6 @@ module.exports = class SpellView extends View
     #console.log "got call index", currentCallIndex, "for time", @thang.world.age, "out of", states.length
 
     # TODO: don't redo the markers if they haven't actually changed
-    text = @aceDoc.getValue()
-    offsetToPos = (offset) ->
-      # TODO: use the nice conversion utils David put into Aether
-      rows = text.substr(0, offset).split '\n'
-      {row: rows.length - 1, column: _.last(rows).length}
-
     for markerRange in (@markerRanges ?= [])
       markerRange.start.detach()
       markerRange.end.detach()
@@ -451,7 +448,7 @@ module.exports = class SpellView extends View
     marked = {}
     lastExecuted = lastExecuted[0 .. @toolbarView.statementIndex] if @toolbarView?.statementIndex?
     for state, i in lastExecuted
-      [start, end] = [offsetToPos(state.range[0]), offsetToPos(state.range[1])]
+      [start, end] = state.range
       clazz = if i is lastExecuted.length - 1 then 'executing' else 'executed'
       if clazz is 'executed'
         continue if marked[start.row]
@@ -460,7 +457,7 @@ module.exports = class SpellView extends View
       else
         @debugView.setVariableStates state.variables
         markerType = "text"
-      markerRange = new Range(start.row, start.column, end.row, end.column)
+      markerRange = new Range start.row, start.col, end.row, end.col
       markerRange.start = @aceDoc.createAnchor markerRange.start
       markerRange.end = @aceDoc.createAnchor markerRange.end
       markerRange.id = @aceSession.addMarker markerRange, clazz, markerType
diff --git a/bower.json b/bower.json
index c89a39fa9..6ecaaad97 100644
--- a/bower.json
+++ b/bower.json
@@ -32,7 +32,7 @@
     "firepad": "~0.1.2",
     "marked": "~0.3.0",
     "moment": "~2.5.0",
-    "aether": "~0.0.9",
+    "aether": "~0.1.2",
     "underscore.string": "~2.3.3",
     "firebase": "~1.0.2"
   },