diff --git a/app/models/Campaign.coffee b/app/models/Campaign.coffee
index 51436acd7..631c95193 100644
--- a/app/models/Campaign.coffee
+++ b/app/models/Campaign.coffee
@@ -11,5 +11,26 @@ module.exports = class Campaign extends CocoModel
   @denormalizedLevelProperties: _.keys(_.omit(schema.properties.levels.additionalProperties.properties, ['unlocks', 'position', 'rewards']))
   @denormalizedCampaignProperties: ['name', 'i18n', 'slug']
   
-  levelsCollection: ->
-    new CocoCollection(_.values(@get('levels')), {model: Level})
+  statsForSessions: (sessions) ->
+    # common code for crunching stats for a user's progress on a campaign/course
+    stats = {}
+    sessions = _.sortBy sessions.models, (s) -> s.get('changed')
+    levels = _.values(@get('levels'))
+    levels = (level for level in levels when not _.contains(level.type, 'ladder'))
+    levelOriginals = _.pluck(levels, 'original')
+    sessionOriginals = (session.get('level').original for session in sessions when session.get('state').complete)
+    levelsLeft = _.size(_.difference(levelOriginals, sessionOriginals))
+    lastSession = _.last(sessions)
+    stats.levels = {
+      size: _.size(levels)
+      left: levelsLeft
+      done: levelsLeft is 0
+      numDone: _.size(levels) - levelsLeft
+      pctDone: (100 * (_.size(levels) - levelsLeft) / _.size(levels)).toFixed(1) + '%'
+      lastPlayed: if lastSession then _.findWhere levels, { original: lastSession.get('level').original } else null
+      first: _.first(levels)
+      arena: _.find _.values(@get('levels')), (level) -> _.contains(level.type, 'ladder')
+    }
+    sum = (nums) -> _.reduce(nums, (s, num) -> s + num)
+    stats.playtime = sum((session.get('playtime') or 0 for session in sessions))
+    return stats
\ No newline at end of file
diff --git a/app/styles/courses/courses-view.sass b/app/styles/courses/courses-view.sass
index 7454f403f..91b46876a 100644
--- a/app/styles/courses/courses-view.sass
+++ b/app/styles/courses/courses-view.sass
@@ -29,3 +29,11 @@
     
   .course-instance-entry
     padding-left: 40px
+    
+    .progress-bar
+      min-width: 15%
+
+    .btn
+      margin-left: 20px
+      min-width: 180px
+      
\ No newline at end of file
diff --git a/app/templates/courses/courses-view.jade b/app/templates/courses/courses-view.jade
index 93890b884..3a9d9240c 100644
--- a/app/templates/courses/courses-view.jade
+++ b/app/templates/courses/courses-view.jade
@@ -28,8 +28,11 @@ block content
     else
       
       - var showHOCComplete = false;
-      if false && view.hocCourseInstance
-        - showHOCComplete = view.hocCourseInstance.sessions.allDone();
+      if view.hocCourseInstance
+        - var course = view.courses.get(view.hocCourseInstance.get('courseID'));
+        - var campaign = view.campaigns.get(course.get('campaignID'));
+        - var stats = campaign.statsForSessions(view.hocCourseInstance.sessions);
+        - showHOCComplete = stats.levels.done;
         
       .text-center
         if !showHOCComplete
@@ -43,7 +46,7 @@ block content
             li Learn even more programming!
           a.btn.btn-lg.btn-success(href="/play")
       
-      if false && view.hocCourseInstance
+      if view.hocCourseInstance && !view.classrooms.size()
         h3 Saved Games
         hr
         
@@ -56,7 +59,7 @@ block content
           +course-instance-body(view.hocCourseInstance)
           .clearfix
         
-      if view.classrooms.size()
+      else if view.classrooms.size()
         h3.text-uppercase My Classes
         hr
         
@@ -111,29 +114,43 @@ block content
 mixin course-instance-body(courseInstance)
   - var course = view.courses.get(courseInstance.get('courseID'));
   - var campaign = view.campaigns.get(course.get('campaignID'));
-  - var levels = campaign.levelsCollection();
-  - var complete = view.isCampaignComplete(campaign, courseInstance.sessions);
-  if complete
+  - var stats = campaign.statsForSessions(courseInstance.sessions);
+  if stats.levels.done
     .text-success
       span.glyphicon.glyphicon-ok
       span.spl Course Complete!
   .pull-right
-    if complete
-      - var arenaLevel = levels.findWhere({ type: 'course-ladder' });
+    if stats.levels.done
+      - var arenaLevel = stats.levels.arena;
       if arenaLevel
-        - var arenaURL = "/play/ladder/"+arenaLevel.get('slug')+"/course/"+courseInstance.id;
+        - var arenaURL = "/play/ladder/"+arenaLevel.slug+"/course/"+courseInstance.id;
         a.btn.btn-warning.btn-lg(href=arenaURL)
           | Play Arena
       else
         a.btn.btn-default.btn-lg(disabled=true) Course Complete
     else if courseInstance.sessions.size()
-      - var lastSession = courseInstance.sessions.last();
-      - var lastLevel = levels.findWhere({original: lastSession.get('level').original});
-      - var levelURL = "/play/level/"+lastLevel.get('slug')+"?course="+courseInstance.get('courseID')+"&course-instance="+courseInstance.id;
+      - var lastLevel = stats.levels.lastPlayed;
+      - var levelURL = "/play/level/"+lastLevel.slug+"?course="+courseInstance.get('courseID')+"&course-instance="+courseInstance.id;
       a.btn.btn-success.btn-lg(href=levelURL)
         | Continue
     else
-      - var firstLevel = levels.first();
-      - var levelURL = "/play/level/"+firstLevel.get('slug')+"?course="+courseInstance.get('courseID')+"&course-instance="+courseInstance.id;
+      - var firstLevel = stats.levels.first;
+      - var levelURL = "/play/level/"+firstLevel.slug+"?course="+courseInstance.get('courseID')+"&course-instance="+courseInstance.id;
       a.btn.btn-info.btn-lg(href=levelURL)
         | Start
+      
+  div
+    span Playtime
+    span.spr :
+    span= moment.duration(stats.playtime, 'seconds').humanize()
+    
+  if stats.levels.lastPlayed
+    div
+      span Last Level
+      span.spr :
+      span= stats.levels.lastPlayed.name
+
+  .progress
+    .progress-bar(style="width:"+stats.levels.pctDone)= stats.levels.pctDone
+    
+  
\ No newline at end of file
diff --git a/app/views/courses/CoursesView.coffee b/app/views/courses/CoursesView.coffee
index 121d5c362..2bb8236c0 100644
--- a/app/views/courses/CoursesView.coffee
+++ b/app/views/courses/CoursesView.coffee
@@ -49,19 +49,12 @@ module.exports = class CoursesView extends RootView
         model: LevelSession 
       })
       courseInstance.sessions.comparator = 'changed'
-      @supermodel.loadCollection(courseInstance.sessions, 'sessions', { data: { project: 'state.complete level.original' }})
+      @supermodel.loadCollection(courseInstance.sessions, 'sessions', { data: { project: 'state.complete level.original playtime changed' }})
       
     @hocCourseInstance = @courseInstances.findWhere({hourOfCode: true})
     if @hocCourseInstance
       @courseInstances.remove(@hocCourseInstance)
 
-  isCampaignComplete: (campaign, sessions) ->
-    levels = _.values(campaign.get('levels'))
-    levels = (level for level in levels when not _.contains(level.type, 'ladder'))
-    levelOriginals = _.pluck(levels, 'original')
-    sessionOriginals = (session.get('level').original for session in sessions.models)
-    return _.size(_.difference(levelOriginals, sessionOriginals)) is 0
-    
   onClickStartNewGameButton: ->
     @openSignUpModal()