diff --git a/app/templates/main.html b/app/templates/main.html
index 6ae5fc799000a4d105e1ba72a65c117a506446eb..ddf29a44ccb1d6fd4c8314aa30443ae9d1a41897 100644
--- a/app/templates/main.html
+++ b/app/templates/main.html
@@ -19,9 +19,16 @@
 		.room {
 			border: 1px solid green;
 		}
-		.hour {
+		.slot {
 			border-bottom: 1px solid green;
 		}
+		.slotlabel {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			margin: 0;
+			font-weight: bold;
+		}
 		.roomhead p {
 			text-align: center;
 			font-weight: bold;
@@ -63,8 +70,9 @@
 		<label for=date>Date:</label>
 		<input id=date type=date name=date value="{{ g.date }}">
 		<select name=hours>
-			<option value=0{{ " selected" if g.hours==0 else "" }}>Working hours</option>
-			<option value=1{{ " selected" if g.hours==1 else "" }}>Whole day</option>
+			<option value=0{{ " selected" if g.hours==0 else "" }}>Study hours</option>
+			<option value=1{{ " selected" if g.hours==1 else "" }}>Working hours</option>
+			<option value=2{{ " selected" if g.hours==2 else "" }}>Whole day</option>
 		</select>
 		<select name=rooms>
 			<option value=i{{ " selected" if g.rooms=="i" else "" }}>CompSci rooms</option>
@@ -86,8 +94,11 @@
 {% for r in g.room_boxes %}
 		<div class=room style='position: absolute; left: {{ r.x }}px; top: 0px; width: {{ r.w }}px; height: {{ r.h }}px;'></div>
 {% endfor %}
-{% for h in g.hour_boxes %}
-		<div class=hour style='position: absolute; left: {{ h.x }}px; top: {{ h.y }}px; width: {{ h.w }}px; height: {{ h.h }}px;'></div>
+{% for h in g.slot_boxes %}
+		<div class=slot style='position: absolute; left: {{ h.x }}px; top: {{ h.y }}px; width: {{ h.w }}px; height: {{ h.h }}px;'></div>
+{% endfor %}
+{% for h in g.slot_labels %}
+		<div class=slotlabel style='position: absolute; left: {{ h.x }}px; top: {{ h.y }}px; width: {{ h.w }}px; height: {{ h.h }}px;'>{{ h.label }}</div>
 {% endfor %}
 {% for m in g.meetings %}
 		<div class='meeting{{ " collision" if m.coll else "" }}' style='position: absolute; left: {{ m.x }}px; top: {{ m.y }}px; width: {{ m.w }}px; height: {{ m.h }}px;' title='{{ m.start + "–" + m.end + ": " + m.topic|e }}'>
diff --git a/app/zoom.py b/app/zoom.py
index 8f4a16c4cbb53a2236313048c024c4a8d03841c8..88db0bdd0cbd58a79c3a18fc08746943950a6e00 100644
--- a/app/zoom.py
+++ b/app/zoom.py
@@ -3,7 +3,7 @@ from flask import Flask, render_template, request, g
 import psycopg2
 import psycopg2.extras
 import time
-from datetime import datetime
+from datetime import datetime, timedelta
 import sys
 import dateutil
 import dateutil.tz
@@ -82,18 +82,23 @@ def main_page():
     g.dow = dt.strftime("%A")
 
     hours_arg = request.args.get("hours", "")
-    if hours_arg in ["0", "1"]:
+    if hours_arg in ["0", "1", "2"]:
         g.hours = int(hours_arg)
     else:
         g.hours = 0
 
     if g.hours == 0:
-        hour_min = 8
-        hour_max = 24
+        slot_size = 100*60
+        first_slot = 9*3600
+        num_slots = 10
+    elif g.hours == 1:
+        slot_size = 3600
+        first_slot = 8*3600
+        num_slots = 16
     else:
-        hour_min = 0
-        hour_max = 24
-    num_hours = hour_max - hour_min
+        slot_size = 3600
+        first_slot = 0
+        num_slots = 24
 
     g.rooms = request.args.get("rooms", "")
     if g.rooms not in room_list:
@@ -104,21 +109,32 @@ def main_page():
     email_to_room_index = { rooms[i][1]: i for i in range(num_rooms) }
     room_box_width = 100
     room_hour_height = 50
+    room_slot_height = room_hour_height * slot_size // 3600
+    row_offset = 70
+
     g.total_width = room_box_width * num_rooms
-    g.total_height = num_hours * room_hour_height
+    g.total_height = num_slots * room_slot_height
     g.room_boxes = [{
-            "x": i * room_box_width + 1,
+            "x": row_offset + i * room_box_width + 1,
             "w": room_box_width - 1,
             "h": g.total_height - 1,
             "name": rooms[i][0],
         } for i in range(num_rooms)]
 
-    g.hour_boxes = [{
-            "x": 1,
-            "y": i * room_hour_height + 1,
-            "w": num_rooms * room_box_width - 1,
-            "h": room_hour_height - 1,
-        } for i in range(num_hours)]
+    g.slot_boxes = [{
+            "x": row_offset + 1,
+            "y": i * room_slot_height + 1,
+            "w": num_rooms * room_box_width,
+            "h": room_slot_height - 1,
+        } for i in range(num_slots)]
+
+    g.slot_labels = [{
+            "x": 0,
+            "y": i * room_slot_height,
+            "w": row_offset - 2,
+            "h": room_slot_height - 1,
+            "label": (dt + timedelta(seconds = first_slot + i*slot_size)).strftime("%H:%M"),
+        } for i in range(num_slots)]
 
     # XXX: No meeting is ever longer than 24 hours
     db_query("""
@@ -143,15 +159,15 @@ def main_page():
         start_dt = r.start_time.replace(tzinfo=dateutil.tz.tz.tzutc())
         start_t = int(start_dt.timestamp())
         end_t = start_t + r.duration*60
-        rel_start = start_t - dt.timestamp() - hour_min*3600
+        rel_start = start_t - dt.timestamp() - first_slot
         rel_end = rel_start + r.duration*60
         start = max(0, int(rel_start))
-        end = min(num_hours * 3600, int(rel_end))
+        end = min(num_slots * slot_size, int(rel_end))
         app.logger.debug("Meeting: %s start_t=%s start=%s end=%s room_i=%s", r, start_t, start, end, i)
         if start < end:
             coll = (i == prev_room_i and start_t < prev_end_t)
             g.meetings.append({
-                "x": i * room_box_width + 4 + 10*int(coll),
+                "x": row_offset + i * room_box_width + 4 + 10*int(coll),
                 "y": int(start / 3600. * room_hour_height),
                 "w": room_box_width - 7 - 10*int(coll),
                 "h": int((end - start) / 3600 * room_hour_height),