   res.sendFile filename
 
 formatList = (list, urlFunc, size) ->
-  return '<style type="text/css">.grid-item { width: ' + (size || '50%') + ' }</style><div class="grid">' + (list.map((s) ->
-    return linkToImage(s, (if urlFunc then urlFunc(s) else null), size)
+  size ||= '50%'
+  return '<style type="text/css">.grid-item { width: ' + size + '; float: left; margin-bottom: 1em }</style><div class="grid">' + (list.map((s, index) ->
+    html = linkToImage(s, (if urlFunc then urlFunc(s) else null), size)
+    if ((size == "33%" && index % 3 == 2) || (size == "50%" && index % 2 == 1))
+      html += '<br clear="both" />'
+    return html
   ).join '') + '</div>'
+
+countTags = (sketches) ->
+  tags = {}
+  regexp = /#([-a-zA-Z]+)/g
+  sketches.map (s) ->
+    while true
+      m = regexp.exec(s)
+      if !m then break
+      tags[m[1]] ||= 0
+      tags[m[1]]++
+  list = ([key, val] for own key, val of tags)
+  return list.sort (a, b) ->
+    return b[1] - a[1]
+    
+serveTagList = (req, res) ->
+  getSketches(SKETCH_DIR).then (sketches) ->
+    nonJournal = sketches.filter((s) ->
+      return !s.match(/#journal/)
+    )
+    tags = countTags(nonJournal)
+    if req.query.sort == 'alpha'
+      tags = tags.sort (a, b) ->
+        return -1 if a[0] < b[0]
+        return 1 if a[0] > b[0]
+        return 0
+    res.send header + '<ul>' + tags.map((tagInfo) ->
+      return '<li><a href="/tag/' + tagInfo[0] + '">' + tagInfo[0] + ' (' + tagInfo[1] + ')</a></li>'
+    ).join('') + '</ul>' + footer
   
 serveImagesByTag = (req, res) ->
   tag = req.params.tag.split(/,/g)

    res.header "Cache-Control", "no-cache, no-store, must-revalidate"
    res.header "Pragma", "no-cache"
    res.header "Expires", 0
    res.send header + formatList(list) + footer

serveImageByID = (req, res) ->
  getSketches(SKETCH_DIR).then (sketches) ->

app.get '/random', serveRandomImage
app.get '/id/:id', serveImageByID
app.get '/image/:filename', serveImageByName
app.get '/tag', serveTagList
app.get '/tag/:tag', serveImagesByTag
app.get '/date/:year/:month?', serveImagesByDate
app.get '/nonjournal', listNonjournalSketches
app.get '/journal', listJournalSketches

app.listen process.env.PORT || 3000, () ->
  console.log "Listening on " + (process.env.PORT || 3000)
