diff --git a/app/assets/javascripts/discourse/models/badge.js b/app/assets/javascripts/discourse/models/badge.js
index 7fa14bf61..4bc61a95e 100644
--- a/app/assets/javascripts/discourse/models/badge.js
+++ b/app/assets/javascripts/discourse/models/badge.js
@@ -167,5 +167,18 @@ Discourse.Badge.reopenClass({
     return Discourse.ajax('/badges.json').then(function(badgesJson) {
       return Discourse.Badge.createFromJson(badgesJson);
     });
+  },
+
+  /**
+    Returns a `Discourse.Badge` that has the given ID.
+
+    @method findById
+    @param {Number} id ID of the badge
+    @returns {Promise} a promise that resolves to a `Discourse.Badge`
+  **/
+  findById: function(id) {
+    return Discourse.ajax("/badges/" + id).then(function(badgeJson) {
+      return Discourse.Badge.createFromJson(badgeJson);
+    });
   }
 });
diff --git a/app/assets/javascripts/discourse/models/user_badge.js b/app/assets/javascripts/discourse/models/user_badge.js
index 3d521c1fa..b19a3a25a 100644
--- a/app/assets/javascripts/discourse/models/user_badge.js
+++ b/app/assets/javascripts/discourse/models/user_badge.js
@@ -54,6 +54,9 @@ Discourse.UserBadge.reopenClass({
     userBadges = userBadges.map(function(userBadgeJson) {
       var userBadge = Discourse.UserBadge.create(userBadgeJson);
       userBadge.set('badge', badges[userBadge.get('badge_id')]);
+      if (userBadge.get('user_id')) {
+        userBadge.set('user', users[userBadge.get('user_id')]);
+      }
       if (userBadge.get('granted_by_id')) {
         userBadge.set('granted_by', users[userBadge.get('granted_by_id')]);
       }
@@ -71,6 +74,7 @@ Discourse.UserBadge.reopenClass({
     Find all badges for a given username.
 
     @method findByUsername
+    @param {String} username
     @returns {Promise} a promise that resolves to an array of `Discourse.UserBadge`.
   **/
   findByUsername: function(username) {
@@ -79,6 +83,19 @@ Discourse.UserBadge.reopenClass({
     });
   },
 
+  /**
+    Find all badge grants for a given badge ID.
+
+    @method findById
+    @param {String} badgeId
+    @returns {Promise} a promise that resolves to an array of `Discourse.UserBadge`.
+  **/
+  findByBadgeId: function(badgeId) {
+    return Discourse.ajax("/user_badges.json?badge_id=" + badgeId).then(function(json) {
+      return Discourse.UserBadge.createFromJson(json);
+    });
+  },
+
   /**
     Grant the badge having id `badgeId` to the user identified by `username`.
 
diff --git a/app/assets/javascripts/discourse/routes/application_routes.js b/app/assets/javascripts/discourse/routes/application_routes.js
index 53fc50ef3..261b92917 100644
--- a/app/assets/javascripts/discourse/routes/application_routes.js
+++ b/app/assets/javascripts/discourse/routes/application_routes.js
@@ -95,5 +95,7 @@ Discourse.Route.buildRoutes(function() {
   this.route('signup', {path: '/signup'});
   this.route('login', {path: '/login'});
 
-  this.route('badges');
+  this.resource('badges', function() {
+    this.route('show', {path: '/:id/:slug'});
+  });
 });
diff --git a/app/assets/javascripts/discourse/routes/badges_route.js b/app/assets/javascripts/discourse/routes/badges_index_route.js
similarity index 68%
rename from app/assets/javascripts/discourse/routes/badges_route.js
rename to app/assets/javascripts/discourse/routes/badges_index_route.js
index 4852fc4be..6563af0d7 100644
--- a/app/assets/javascripts/discourse/routes/badges_route.js
+++ b/app/assets/javascripts/discourse/routes/badges_index_route.js
@@ -1,12 +1,12 @@
 /**
   Shows a list of all badges.
 
-  @class BadgesRoute
+  @class BadgesIndexRoute
   @extends Discourse.Route
   @namespace Discourse
   @module Discourse
 **/
-Discourse.BadgesRoute = Discourse.Route.extend({
+Discourse.BadgesIndexRoute = Discourse.Route.extend({
   model: function() {
     return Discourse.Badge.findAll();
   }
diff --git a/app/assets/javascripts/discourse/routes/badges_show_route.js b/app/assets/javascripts/discourse/routes/badges_show_route.js
new file mode 100644
index 000000000..d07693250
--- /dev/null
+++ b/app/assets/javascripts/discourse/routes/badges_show_route.js
@@ -0,0 +1,24 @@
+/**
+  Shows a particular badge.
+
+  @class BadgesShowRoute
+  @extends Discourse.Route
+  @namespace Discourse
+  @module Discourse
+**/
+Discourse.BadgesShowRoute = Ember.Route.extend({
+  serialize: function(model) {
+    return {id: model.get('id'), slug: model.get('name').replace(/[^A-Za-z0-9_]+/g, '-').toLowerCase()};
+  },
+
+  model: function(params) {
+    return Discourse.Badge.findById(params.id);
+  },
+
+  setupController: function(controller, model) {
+    Discourse.UserBadge.findByBadgeId(model.get('id')).then(function(userBadges) {
+      controller.set('userBadges', userBadges);
+    });
+    controller.set('model', model);
+  }
+});
diff --git a/app/assets/javascripts/discourse/templates/badges.js.handlebars b/app/assets/javascripts/discourse/templates/badges/index.js.handlebars
similarity index 100%
rename from app/assets/javascripts/discourse/templates/badges.js.handlebars
rename to app/assets/javascripts/discourse/templates/badges/index.js.handlebars
diff --git a/app/assets/javascripts/discourse/templates/badges/show.js.handlebars b/app/assets/javascripts/discourse/templates/badges/show.js.handlebars
new file mode 100644
index 000000000..ce161dcf0
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/badges/show.js.handlebars
@@ -0,0 +1,27 @@
+<div class='container'>
+  <h1>
+    {{#link-to 'badges.index'}}{{i18n badges.title}}{{/link-to}}
+    <i class='fa fa-angle-right'></i>
+    {{name}}
+  </h1>
+
+  <table class='badges-listing'>
+    <tr>
+      <td class='badge'>{{user-badge badge=this}}</td>
+      <td class='description'>{{description}}</td>
+      <td class='grant-count'>{{i18n badges.awarded count=grant_count}}</td>
+    </tr>
+  </table>
+
+  {{#if userBadges}}
+    <h2>{{i18n users}}</h2>
+    <br>
+    {{#each userBadges}}
+      {{#link-to 'userActivity' user}}
+        {{avatar user imageSize="large"}}
+      {{/link-to}}
+    {{/each}}
+  {{else}}
+    <div class='spinner'>{{i18n loading}}</div>
+  {{/if}}
+</div>
diff --git a/app/assets/javascripts/discourse/templates/components/user-badge.js.handlebars b/app/assets/javascripts/discourse/templates/components/user-badge.js.handlebars
index b934b3b1f..ca2070b7e 100644
--- a/app/assets/javascripts/discourse/templates/components/user-badge.js.handlebars
+++ b/app/assets/javascripts/discourse/templates/components/user-badge.js.handlebars
@@ -1,4 +1,6 @@
-<span {{bind-attr class=":user-badge badgeTypeClassName" data-badge-name="badge.name" title="badge.description"}}>
-  <i class='fa fa-certificate'></i>
-  {{badge.name}}
-</span>
+{{#link-to 'badges.show' badge}}
+  <span {{bind-attr class=":user-badge badgeTypeClassName" data-badge-name="badge.name" title="badge.description"}}>
+    <i class='fa fa-certificate'></i>
+    {{badge.name}}
+  </span>
+{{/link-to}}
diff --git a/app/assets/stylesheets/desktop/user-badges.scss b/app/assets/stylesheets/desktop/user-badges.scss
index 9141d4f0e..941e715a2 100644
--- a/app/assets/stylesheets/desktop/user-badges.scss
+++ b/app/assets/stylesheets/desktop/user-badges.scss
@@ -1,5 +1,6 @@
 .user-badge {
   padding: 3px 8px;
+  color: $primary_text_color;
   border: 1px solid $secondary-border-color;
   font-size: $base-font-size * 0.86;
   line-height: 16px;
@@ -27,7 +28,7 @@
 }
 
 table.badges-listing {
-  margin-top: 20px;
+  margin: 20px 0;
   border-bottom: 1px solid $primary-border-color;
 
   .user-badge {
diff --git a/app/controllers/badges_controller.rb b/app/controllers/badges_controller.rb
index c256cbed6..a8908afd4 100644
--- a/app/controllers/badges_controller.rb
+++ b/app/controllers/badges_controller.rb
@@ -3,4 +3,10 @@ class BadgesController < ApplicationController
     badges = Badge.all.to_a
     render_serialized(badges, BadgeSerializer, root: "badges")
   end
+
+  def show
+    params.require(:id)
+    badge = Badge.find(params[:id])
+    render_serialized(badge, BadgeSerializer, root: "badge")
+  end
 end
diff --git a/app/controllers/user_badges_controller.rb b/app/controllers/user_badges_controller.rb
index ef5bbf334..daba1ddc9 100644
--- a/app/controllers/user_badges_controller.rb
+++ b/app/controllers/user_badges_controller.rb
@@ -1,8 +1,14 @@
 class UserBadgesController < ApplicationController
   def index
-    params.require(:username)
-    user = fetch_user_from_params
-    render_serialized(user.user_badges, UserBadgeSerializer, root: "user_badges")
+    params.permit(:username)
+    if params[:username]
+      user = fetch_user_from_params
+      user_badges = user.user_badges
+    else
+      badge = fetch_badge_from_params
+      user_badges = badge.user_badges.order('granted_at DESC').limit(20).to_a
+    end
+    render_serialized(user_badges, UserBadgeSerializer, root: "user_badges")
   end
 
   def create
diff --git a/app/serializers/user_badge_serializer.rb b/app/serializers/user_badge_serializer.rb
index d91dfb916..eda18b11e 100644
--- a/app/serializers/user_badge_serializer.rb
+++ b/app/serializers/user_badge_serializer.rb
@@ -1,6 +1,7 @@
 class UserBadgeSerializer < ApplicationSerializer
   attributes :id, :granted_at
 
+  has_one :user
   has_one :badge
   has_one :granted_by, serializer: BasicUserSerializer, root: :users
 end
diff --git a/config/routes.rb b/config/routes.rb
index 3afb6af37..2bf6bfa35 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -243,6 +243,7 @@ Discourse::Application.routes.draw do
   resources :user_actions
 
   resources :badges, only: [:index]
+  get "/badges/:id(/:slug)" => "badges#show"
   resources :user_badges, only: [:index, :create, :destroy]
 
   # We've renamed popular to latest. If people access it we want a permanent redirect.
diff --git a/spec/controllers/badges_controller_spec.rb b/spec/controllers/badges_controller_spec.rb
index 60bd0ed36..945d64151 100644
--- a/spec/controllers/badges_controller_spec.rb
+++ b/spec/controllers/badges_controller_spec.rb
@@ -12,4 +12,13 @@ describe BadgesController do
       parsed["badges"].length.should == 1
     end
   end
+
+  context 'show' do
+    it "should return a badge" do
+      xhr :get, :show, id: badge.id
+      response.status.should == 200
+      parsed = JSON.parse(response.body)
+      parsed["badge"].should be_present
+    end
+  end
 end
diff --git a/spec/controllers/user_badges_controller_spec.rb b/spec/controllers/user_badges_controller_spec.rb
index 311d448f4..87fc240e6 100644
--- a/spec/controllers/user_badges_controller_spec.rb
+++ b/spec/controllers/user_badges_controller_spec.rb
@@ -7,17 +7,25 @@ describe UserBadgesController do
   context 'index' do
     let!(:user_badge) { UserBadge.create(badge: badge, user: user, granted_by: Discourse.system_user, granted_at: Time.now) }
 
-    it 'requires username to be specified' do
+    it 'requires username or badge_id to be specified' do
       expect { xhr :get, :index }.to raise_error
     end
 
-    it 'returns the user\'s badges' do
+    it 'returns user_badges for a user' do
       xhr :get, :index, username: user.username
 
       response.status.should == 200
       parsed = JSON.parse(response.body)
       parsed["user_badges"].length.should == 1
     end
+
+    it 'returns user_badges for a badge' do
+      xhr :get, :index, badge_id: badge.id
+
+      response.status.should == 200
+      parsed = JSON.parse(response.body)
+      parsed["user_badges"].length.should == 1
+    end
   end
 
   context 'create' do
diff --git a/test/javascripts/models/user_badge_test.js b/test/javascripts/models/user_badge_test.js
index eb56100df..9c7c94de6 100644
--- a/test/javascripts/models/user_badge_test.js
+++ b/test/javascripts/models/user_badge_test.js
@@ -25,6 +25,14 @@ test('findByUsername', function() {
   ok(Discourse.ajax.calledOnce, "makes an AJAX call");
 });
 
+test('findByBadgeId', function() {
+  this.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve(multipleBadgesJson));
+  Discourse.UserBadge.findByBadgeId(880).then(function(badges) {
+    ok(Array.isArray(badges), "returns an array");
+  });
+  ok(Discourse.ajax.calledOnce, "makes an AJAX call");
+});
+
 test('grant', function() {
   this.stub(Discourse, 'ajax').returns(Ember.RSVP.resolve(singleBadgeJson));
   Discourse.UserBadge.grant(1, "username").then(function(userBadge) {