diff --git a/app/lib/AudioPlayer.coffee b/app/lib/AudioPlayer.coffee
index eec0deb48..3034df733 100644
--- a/app/lib/AudioPlayer.coffee
+++ b/app/lib/AudioPlayer.coffee
@@ -7,7 +7,13 @@ cache = {}
 rot13 = (s) -> s.replace /[A-z]/g, (c) -> String.fromCharCode c.charCodeAt(0) + (if c.toUpperCase() <= 'M' then 13 else -13)
 swears = (rot13 s for s in ['nefrubyr', 'nffubyr', 'onfgneq', 'ovgpu', 'oybbql', 'obyybpxf', 'ohttre', 'pbpx', 'penc', 'phag', 'qnza', 'qnea', 'qvpx', 'qbhpur', 'snt', 'shpx', 'cvff', 'chffl', 'fuvg', 'fyhg', 'svqqyrfgvpxf'])
 
-createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.FlashPlugin, createjs.HTMLAudioPlugin])
+# IE(11, 10, 9) throws an exception if createjs.FlashPlugin is undefined
+# Chrome and Firefox don't seem to care that it's undefined
+if createjs.FlashPlugin?
+  soundPlugins = [createjs.WebAudioPlugin, createjs.FlashPlugin, createjs.HTMLAudioPlugin]
+else
+  soundPlugins = [createjs.WebAudioPlugin, createjs.HTMLAudioPlugin]
+createjs.Sound.registerPlugins(soundPlugins)
 
 class Manifest
   constructor: -> @storage = {}
diff --git a/app/locale/en.coffee b/app/locale/en.coffee
index 8f0069d6f..68bf777c3 100644
--- a/app/locale/en.coffee
+++ b/app/locale/en.coffee
@@ -52,12 +52,13 @@
     players: "players"  # Hover over a level on /play
     hours_played: "hours played"  # Hover over a level on /play
     items: "Items"  # Tooltip on item shop button from /play
-    unlock: "Unlock" # For purchasing items and heroes
+    unlock: "Unlock"  # For purchasing items and heroes
     confirm: "Confirm"
-    owned: "Owned" # For items you own
+    owned: "Owned"  # For items you own
     locked: "Locked"
+    purchasable: "Purchasable"  # For a hero you unlocked but haven't purchased
     available: "Available"
-    skills_granted: "Skills Granted" # Property documentation details
+    skills_granted: "Skills Granted"  # Property documentation details
     heroes: "Heroes"  # Tooltip on hero shop button from /play
     achievements: "Achievements"  # Tooltip on achievement list button from /play
     account: "Account"  # Tooltip on account button from /play
@@ -350,9 +351,11 @@
     health: "Health"
     speed: "Speed"
     regeneration: "Regeneration"
-    range: "Range" # As in "attack or visual range"
-    blocks: "Blocks" # As in "this shield blocks this much damage"
+    range: "Range"  # As in "attack or visual range"
+    blocks: "Blocks"  # As in "this shield blocks this much damage"
     skills: "Skills"
+    available_for_purchase: "Available for Purchase"
+    level_to_unlock: "Level to unlock:"
 
   skill_docs:
     writable: "writable"  # Hover over "attack" in Your Skills while playing a level to see most of this
diff --git a/app/models/User.coffee b/app/models/User.coffee
index 4896c98bb..eb99993d5 100644
--- a/app/models/User.coffee
+++ b/app/models/User.coffee
@@ -86,8 +86,8 @@ module.exports = class User extends CocoModel
     gemsEarned + gemsPurchased - gemsSpent
 
   heroes: ->
-    heroes = (me.get('earned')?.heroes ? []).concat(me.get('purchased')?.heroes ? []).concat([ThangType.heroes.captain, ThangType.heroes.knight])
-    heroes = _.values ThangType.heroes if me.isAdmin()
+    heroes = (me.get('purchased')?.heroes ? []).concat([ThangType.heroes.captain, ThangType.heroes.knight])
+    #heroes = _.values ThangType.heroes if me.isAdmin()
     heroes
   items: -> (me.get('earned')?.items ? []).concat(me.get('purchased')?.items ? []).concat([ThangType.items['simple-boots']])
   levels: -> (me.get('earned')?.levels ? []).concat(me.get('purchased')?.levels ? [])
diff --git a/app/schemas/models/thang_type.coffee b/app/schemas/models/thang_type.coffee
index b32713a6c..75a8a6d6b 100644
--- a/app/schemas/models/thang_type.coffee
+++ b/app/schemas/models/thang_type.coffee
@@ -149,8 +149,9 @@ _.extend ThangTypeSchema.properties,
       type: 'number'
       description: 'Snap to this many meters in the y-direction.'
   components: c.array {title: 'Components', description: 'Thangs are configured by changing the Components attached to them.', uniqueItems: true, format: 'thang-components-array'}, ThangComponentSchema  # TODO: uniqueness should be based on 'original', not whole thing
-  i18n: {type: 'object', format: 'i18n', props: ['name', 'description', 'extendedName'], description: 'Help translate this ThangType\'s name and description.'}
+  i18n: {type: 'object', format: 'i18n', props: ['name', 'description', 'extendedName', 'unlockLevelName'], description: 'Help translate this ThangType\'s name and description.'}
   extendedName: {type: 'string', title: 'Extended Hero Name', description: 'The long form of the hero\'s name. Ex.: "Captain Anya Weston".'}
+  unlockLevelName: {type: 'string', title: 'Unlock Level Name', description: 'The name of the level in which the hero is unlocked.'}
 
 ThangTypeSchema.required = []
 
diff --git a/app/styles/play/modal/play-heroes-modal.sass b/app/styles/play/modal/play-heroes-modal.sass
index 3138762ef..5359c5487 100644
--- a/app/styles/play/modal/play-heroes-modal.sass
+++ b/app/styles/play/modal/play-heroes-modal.sass
@@ -91,8 +91,9 @@ $heroCanvasHeight: 265px
             left: 21px
             top: 18px
 
-          &.locked
+          &.locked:not(.purchasable)
             .hero-avatar
+              background-color: goldenrod
               @include filter(contrast(50%) brightness(65%))
 
             .lock-indicator
@@ -102,6 +103,10 @@ $heroCanvasHeight: 265px
               top: 30%
               @include filter(invert(90%))
 
+          &.purchasable
+            .hero-avatar
+              background-color: goldenrod
+
 
   //- Small transformations to jumble the hero icons a little
   
@@ -142,7 +147,7 @@ $heroCanvasHeight: 265px
   #hero-carousel
     .hero-item
 
-      &.locked
+      &.locked:not(.purchasable)
         @include opacity(0.6)
 
       canvas, .hero-feature-image
@@ -236,6 +241,60 @@ $heroCanvasHeight: 265px
       .glyphicon
         @include scaleXY(-1, 1)
 
+  //- Different footer states
+
+  #purchasable-hero-explanation
+    position: absolute
+    left: 32px
+    top: 532px
+    width: 541px
+    height: 97px
+    padding: 10px 40px
+
+    h2
+      color: #333
+      text-align: center
+
+  #locked-hero-explanation
+    position: absolute
+    left: 32px
+    top: 527px
+    width: 541px
+    height: 102px
+    padding: 10px 40px
+    text-align: center
+
+    h2
+      color: #333
+      margin-top: 12px
+      margin-bottom: 5px
+
+  #purchase-hero-button
+    width: 209px
+    height: 110px
+    position: absolute
+    left: 588px
+    top: 522px
+    line-height: 100px
+    text-align: center
+    text-transform: uppercase
+    font-size: 24.5px
+    font-family: Open Sans Condensed
+    color: white
+    border: 3px solid rgb(7,65,83)
+    background: rgb(0,119,168)
+    border-radius: 0
+    
+    &:disabled
+      background: rgb(72, 106, 113)
+      opacity: 1
+      color: rgba(255,255,255, 0.4)
+
+    > *
+      @include opacity(0.9)
+
+    &:hover > *
+      @include opacity(1)
 
   //- Programming select box
   
diff --git a/app/templates/game-menu/inventory-modal.jade b/app/templates/game-menu/inventory-modal.jade
index 66a6e7397..8026c5399 100644
--- a/app/templates/game-menu/inventory-modal.jade
+++ b/app/templates/game-menu/inventory-modal.jade
@@ -9,7 +9,7 @@
       span.glyphicon.glyphicon-remove
 
     #equipped
-      if selectedHero
+      if selectedHero && selectedHero.get('featureImage')
         img(src="/file/"+selectedHero.get('featureImage'))#hero-image
 
       for slot in ['head', 'eyes', 'neck', 'torso', 'gloves', 'wrists', 'left-hand', 'right-hand', 'waist', 'feet', 'left-ring', 'right-ring', 'minion', 'flag', 'pet', 'programming-book', 'misc-0', 'misc-1']
diff --git a/app/templates/play/modal/play-heroes-modal.jade b/app/templates/play/modal/play-heroes-modal.jade
index 92c06b6b6..0ebacf09e 100644
--- a/app/templates/play/modal/play-heroes-modal.jade
+++ b/app/templates/play/modal/play-heroes-modal.jade
@@ -12,13 +12,13 @@
       .carousel-indicator-container
         ol.carousel-indicators
           for hero, index in heroes
-            li(data-hero-id=hero.get('original'), title=hero.name, data-slide-to=index, data-target="#hero-carousel", class="hero-indicator hero-index-" + index + (hero.locked ? " locked" : ""))
+            li(data-hero-id=hero.get('original'), title=hero.name, data-slide-to=index, data-target="#hero-carousel", class="hero-indicator hero-index-" + index + (hero.locked ? " locked" : "") + (hero.purchasable ? " purchasable" : ""))
               .hero-avatar
-              if hero.locked
+              if hero.locked && !hero.purchasable
                 img.lock-indicator(src="/images/pages/game-menu/lock.png", draggable="false")
       .carousel-inner
         for hero in heroes
-          div(class="item hero-item" + (hero.locked ? " locked" : ""), data-hero-id=hero.get('original'))
+          div(class="item hero-item" + (hero.locked ? " locked" : "") + (hero.purchasable ? " purchasable" : ""), data-hero-id=hero.get('original'))
             canvas.hero-canvas
             .hero-feature-image
               img(draggable="false")
@@ -28,7 +28,7 @@
               
               .hero-stat-row
                 .stat-label(data-i18n='choose_hero.status')
-                .stat-value(data-i18n=hero.locked ? 'play.locked' : 'play.available')
+                .stat-value(data-i18n=hero.purchasable ? 'play.purchasable' : (hero.locked ? 'play.locked' : 'play.available'))
                 
               .hero-stat-row
                 .stat-label(data-i18n='choose_hero.weapons')
@@ -50,14 +50,33 @@
         span.glyphicon.glyphicon-play
       a.right(role="button", data-slide="next", href="#hero-carousel")
         span.glyphicon.glyphicon-play
-    
-    
-    .form
-      .form-group.select-group
-        span.help-block(data-i18n="choose_hero.programming_language_description") Which programming language do you want to use?
-        //label.control-label(for="option-code-language", data-i18n="choose_hero.programming_language") Programming Language
-        select#option-code-language(name="code-language")
-          for option in codeLanguages
-            option(value=option.id, selected=codeLanguage === option.id)= option.name
-            
-    a#confirm-button(data-i18n=confirmButtonI18N)
+
+    #hero-footer
+      if visibleHero
+        if visibleHero.purchasable
+          #purchasable-hero-explanation
+            h2(data-i18n="choose_hero.available_for_purchase") Available for Purchase
+          button.btn.unlock-button#purchase-hero-button
+            span.spr(data-i18n="play.unlock") Unlock
+            span= visibleHero.get('gems')
+            span.gem.gem-20
+  
+        else if visibleHero.locked
+          //#locked-hero-explanation= lockedExplanation
+          #locked-hero-explanation
+            h2
+              span= visibleHero.name
+              span.spl(data-i18n="play.locked") Locked
+            span.spr(data-i18n="choose_hero.level_to_unlock") Level to unlock:
+            strong= visibleHero.unlockLevelName || '???'
+  
+        else if visibleHero.loaded
+          .form
+            .form-group.select-group
+              span.help-block(data-i18n="choose_hero.programming_language_description") Which programming language do you want to use?
+              //label.control-label(for="option-code-language", data-i18n="choose_hero.programming_language") Programming Language
+              select#option-code-language(name="code-language")
+                for option in codeLanguages
+                  option(value=option.id, selected=codeLanguage === option.id)= option.name
+                  
+          a#confirm-button(data-i18n=confirmButtonI18N)
diff --git a/app/views/i18n/I18NEditThangTypeView.coffee b/app/views/i18n/I18NEditThangTypeView.coffee
index 70698bd55..1c3c07012 100644
--- a/app/views/i18n/I18NEditThangTypeView.coffee
+++ b/app/views/i18n/I18NEditThangTypeView.coffee
@@ -14,3 +14,4 @@ module.exports = class ThangTypeI18NView extends I18NEditModelView
       @wrapRow('Name', ['name'], name, i18n[lang]?.name, [])
       @wrapRow('Description', ['description'], @model.get('description'), i18n[lang]?.description, [], 'markdown')
       @wrapRow('Extended Hero Name', ['extendedName'], @model.get('extendedName'), i18n[lang]?.extendedName, [])
+      @wrapRow('Unlock Level Name', ['unlockLevelName'], @model.get('unlockLevelName'), i18n[lang]?.unlockLevelName, [])
diff --git a/app/views/play/modal/PlayHeroesModal.coffee b/app/views/play/modal/PlayHeroesModal.coffee
index c4f98019a..6400a9406 100644
--- a/app/views/play/modal/PlayHeroesModal.coffee
+++ b/app/views/play/modal/PlayHeroesModal.coffee
@@ -28,23 +28,28 @@ module.exports = class PlayHeroesModal extends ModalView
     @confirmButtonI18N = options.confirmButtonI18N ? "common.save"
     @heroes = new CocoCollection([], {model: ThangType})
     @heroes.url = '/db/thang.type?view=heroes'
-    @heroes.setProjection ['original','name','slug','soundTriggers','featureImage','gems','heroClass','description','components','extendedName','i18n']
+    @heroes.setProjection ['original','name','slug','soundTriggers','featureImage','gems','heroClass','description','components','extendedName','unlockLevelName','i18n']
     @heroes.comparator = 'gems'
     @listenToOnce @heroes, 'sync', @onHeroesLoaded
     @supermodel.loadCollection(@heroes, 'heroes')
     @stages = {}
     @session = options.session
     @initCodeLanguageList options.hadEverChosenHero
+    @heroAnimationInterval = setInterval @animateHeroes, 2500
 
   onHeroesLoaded: ->
-    for hero in @heroes.models
-      hero.name = utils.i18n hero.attributes, 'extendedName' # or whatever the property name ends up being
-      hero.name ?= utils.i18n hero.attributes, 'name'
-      hero.description = utils.i18n hero.attributes, 'description'
-      original = hero.get('original')
-      hero.locked = original not in [ThangType.heroes.captain, ThangType.heroes.knight] and not me.ownsHero(original)
-      hero.class = (hero.get('heroClass') or 'warrior').toLowerCase()
-      hero.stats = hero.getHeroStats()
+    @formatHero hero for hero in @heroes.models
+
+  formatHero: (hero) ->
+    hero.name = utils.i18n hero.attributes, 'extendedName' # or whatever the property name ends up being
+    hero.name ?= utils.i18n hero.attributes, 'name'
+    hero.description = utils.i18n hero.attributes, 'description'
+    hero.unlockLevelName = utils.i18n hero.attributes, 'unlockLevelName'
+    original = hero.get('original')
+    hero.locked = not me.ownsHero(original)
+    hero.purchasable = hero.locked and (original in (me.get('earned')?.heroes ? []))
+    hero.class = (hero.get('heroClass') or 'warrior').toLowerCase()
+    hero.stats = hero.getHeroStats()
 
   getRenderData: (context={}) ->
     context = super(context)
@@ -53,6 +58,7 @@ module.exports = class PlayHeroesModal extends ModalView
     context.codeLanguages = @codeLanguageList
     context.codeLanguage = @codeLanguage = @options?.session?.get('codeLanguage') ? me.get('aceConfig')?.language ? 'python'
     context.confirmButtonI18N = @confirmButtonI18N
+    context.visibleHero = @visibleHero
     context
 
   afterRender: ->
@@ -73,6 +79,11 @@ module.exports = class PlayHeroesModal extends ModalView
     @buildCodeLanguages()
     Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1
 
+  rerenderFooter: ->
+    @formatHero @visibleHero
+    @renderSelectors '#hero-footer'
+    @buildCodeLanguages()
+
   initCodeLanguageList: (hadEverChosenHero) ->
     @codeLanguageList = [
       {id: 'python', name: "Python (#{$.i18n.t('choose_hero.default')})"}
@@ -93,7 +104,8 @@ module.exports = class PlayHeroesModal extends ModalView
     @preloadHero heroIndex + 1
     @preloadHero heroIndex - 1
     @selectedHero = hero unless hero.locked
-    $('#choose-inventory-button').prop 'disabled', hero.locked
+    @visibleHero = hero
+    @rerenderFooter()
     @trigger 'hero-loaded', {hero: hero}
 
   getFullHero: (original) ->
@@ -111,7 +123,8 @@ module.exports = class PlayHeroesModal extends ModalView
 
   loadHero: (hero, heroIndex, preloading=false) ->
     createjs.Ticker.removeEventListener 'tick', stage for stage in _.values @stages
-    if featureImage = hero.get 'featureImage'
+    # TODO: make sure we are going to axe featureImage, then remove this
+    if false and featureImage = hero.get 'featureImage'
       $(".hero-item[data-hero-id='#{hero.get('original')}'] canvas").hide()
       $(".hero-item[data-hero-id='#{hero.get('original')}'] .hero-feature-image").show().find('img').prop('src', '/file/' + featureImage)
       @playSelectionSound hero unless preloading
@@ -129,27 +142,53 @@ module.exports = class PlayHeroesModal extends ModalView
       builder = new SpriteBuilder(fullHero)
       movieClip = builder.buildMovieClip(fullHero.get('actions').attack?.animation ? fullHero.get('actions').idle.animation)
       movieClip.scaleX = movieClip.scaleY = canvas.prop('height') / 120  # Average hero height is ~110px tall at normal resolution
-      if fullHero.get('name') in ['Knight', 'Robot Walker']  # These are too big, so shrink them.
-        movieClip.scaleX *= 0.7
-        movieClip.scaleY *= 0.7
       movieClip.regX = -fullHero.get('positions').registration.x
       movieClip.regY = -fullHero.get('positions').registration.y
       movieClip.x = canvas.prop('width') * 0.5
       movieClip.y = canvas.prop('height') * 0.925  # This is where the feet go.
+      if fullHero.get('name') is 'Knight'
+        movieClip.scaleX *= 0.7
+        movieClip.scaleY *= 0.7
+      if fullHero.get('name') is 'Potion Master'
+        movieClip.scaleX *= 0.9
+        movieClip.scaleY *= 0.9
+        movieClip.regX *= 1.1
+        movieClip.regY *= 1.4
+      if fullHero.get('name') is 'Samurai'
+        movieClip.scaleX *= 0.7
+        movieClip.scaleY *= 0.7
+        movieClip.regX *= 1.2
+        movieClip.regY *= 1.35
+      if fullHero.get('name') is 'Librarian'
+        movieClip.regX *= 0.7
+        movieClip.regY *= 1.2
+      if fullHero.get('name') is 'Sorcerer'
+        movieClip.scaleX *= 0.9
+        movieClip.scaleY *= 0.9
+        movieClip.regX *= 1.15
+        movieClip.regY *= 1.3
+
       stage = new createjs.Stage(canvas[0])
       @stages[heroIndex] = stage
       stage.addChild movieClip
       stage.update()
+      movieClip.loop = false
       movieClip.gotoAndPlay 0
       unless preloading
         createjs.Ticker.addEventListener 'tick', stage
         @playSelectionSound hero
+      @rerenderFooter()
     if fullHero.loaded
       _.defer onLoaded
     else
       @listenToOnce fullHero, 'sync', onLoaded
     fullHero
 
+  animateHeroes: =>
+    return unless @visibleHero
+    heroIndex = Math.max 0, _.findIndex(@heroes.models, ((hero) => hero.get('original') is @visibleHero.get('original')))
+    @stages[heroIndex]?.children?[0]?.gotoAndPlay? 0
+
   playSelectionSound: (hero) ->
     return if @$el.hasClass 'secret'
     @currentSoundInstance?.stop()
@@ -208,6 +247,7 @@ module.exports = class PlayHeroesModal extends ModalView
     Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1
 
   destroy: ->
+    clearInterval @heroAnimationInterval
     for heroIndex, stage of @stages
       createjs.Ticker.removeEventListener "tick", stage
       stage.removeAllChildren()
diff --git a/server/levels/thangs/thang_type_handler.coffee b/server/levels/thangs/thang_type_handler.coffee
index 537058fbd..088d6a454 100644
--- a/server/levels/thangs/thang_type_handler.coffee
+++ b/server/levels/thangs/thang_type_handler.coffee
@@ -31,6 +31,7 @@ ThangTypeHandler = class ThangTypeHandler extends Handler
     'heroClass'
     'tier'
     'extendedName'
+    'unlockLevelName'
   ]
 
   hasAccess: (req) ->
diff --git a/server/queues/scoring.coffee b/server/queues/scoring.coffee
index bfe3793d2..4a4c78687 100644
--- a/server/queues/scoring.coffee
+++ b/server/queues/scoring.coffee
@@ -22,7 +22,7 @@ connectToScoringQueue = ->
     queues.queueClient.registerQueue 'scoring', {}, (error, data) ->
       if error? then throw new Error "There was an error registering the scoring queue: #{error}"
       scoringTaskQueue = data
-      log.info 'Connected to scoring task queue!'
+      #log.info 'Connected to scoring task queue!'
 
 module.exports.messagesInQueueCount = (req, res) ->
   scoringTaskQueue.totalMessagesInQueue (err, count) ->
@@ -189,10 +189,10 @@ module.exports.getTwoGames = (req, res) ->
                 'creator': session.creator
                 'totalScore': session.totalScore
               taskObject.sessions.push sessionInformation
-            console.log 'Dispatching random game between', taskObject.sessions[0].creatorName, 'and', taskObject.sessions[1].creatorName
+            #console.log 'Dispatching random game between', taskObject.sessions[0].creatorName, 'and', taskObject.sessions[1].creatorName
             sendResponseObject req, res, taskObject
   else
-    console.log "Directly simulating #{humansGameID} vs. #{ogresGameID}."
+    #console.log "Directly simulating #{humansGameID} vs. #{ogresGameID}."
     LevelSession.findOne(_id: humansGameID).select(selection).lean().exec (err, humanSession) =>
       if err? then return errors.serverError(res, 'Couldn\'t find the human game')
       LevelSession.findOne(_id: ogresGameID).select(selection).lean().exec (err, ogreSession) =>
@@ -217,7 +217,7 @@ module.exports.getTwoGames = (req, res) ->
 
 module.exports.recordTwoGames = (req, res) ->
   sessions = req.body.sessions
-  console.log 'Recording non-chained result of', sessions?[0]?.name, sessions[0]?.metrics?.rank, 'and', sessions?[1]?.name, sessions?[1]?.metrics?.rank
+  #console.log 'Recording non-chained result of', sessions?[0]?.name, sessions[0]?.metrics?.rank, 'and', sessions?[1]?.name, sessions?[1]?.metrics?.rank
 
   yetiGuru = clientResponseObject: req.body, isRandomMatch: true
   async.waterfall [
@@ -608,7 +608,7 @@ updateMatchesInSession = (matchObject, sessionID, callback) ->
 updateUserSimulationCounts = (reqUserID, callback) ->
   incrementUserSimulationCount reqUserID, 'simulatedBy', (err) =>
     if err? then return callback err
-    console.log 'Incremented user simulation count!'
+    #console.log 'Incremented user simulation count!'
     unless @isRandomMatch
       incrementUserSimulationCount @levelSession.creator, 'simulatedFor', callback
     else
@@ -648,7 +648,7 @@ determineIfSessionShouldContinueAndUpdateLog = (cb) ->
       ratio = (updatedSession.numberOfLosses) / (totalNumberOfGamesPlayed)
       if ratio > 0.33
         cb 'shouldn\'t continue'
-        console.log "Ratio(#{ratio}) is bad, ending simulation"
+        #console.log "Ratio(#{ratio}) is bad, ending simulation"
       else
         #console.log "Ratio(#{ratio}) is good, so continuing simulations"
         cb null
diff --git a/server_setup.coffee b/server_setup.coffee
index 7120c22db..c8cf1631e 100644
--- a/server_setup.coffee
+++ b/server_setup.coffee
@@ -25,7 +25,7 @@ productionLogging = (tokens, req, res) ->
   else if status >= 300 then color = 36
   elapsed = (new Date()) - req._startTime
   elapsedColor = if elapsed < 500 then 90 else 31
-  if (status isnt 200 and status isnt 204 and status isnt 304 and status isnt 302) or elapsed > 500
+  if (status isnt 200 and status isnt 201 and status isnt 204 and status isnt 304 and status isnt 302) or elapsed > 500
     return "\x1b[90m#{req.method} #{req.originalUrl} \x1b[#{color}m#{res.statusCode} \x1b[#{elapsedColor}m#{elapsed}ms\x1b[0m"
   null