diff --git a/vipra-backend/src/main/java/de/vipra/rest/resource/ArticleResource.java b/vipra-backend/src/main/java/de/vipra/rest/resource/ArticleResource.java
index d25cb8d0b7ede3ea6874b37be48a8ab487c21073..acc0312b59c27e5c41eafccbcd882da3863570f7 100644
--- a/vipra-backend/src/main/java/de/vipra/rest/resource/ArticleResource.java
+++ b/vipra-backend/src/main/java/de/vipra/rest/resource/ArticleResource.java
@@ -52,8 +52,7 @@ public class ArticleResource {
 	@GET
 	@Produces(MediaType.APPLICATION_JSON)
 	public Response getArticles(@QueryParam("skip") final Integer skip, @QueryParam("limit") final Integer limit,
-			@QueryParam("sort") @DefaultValue("date") final String sortBy, @QueryParam("fields") final String fields,
-			@QueryParam("query") final String query) {
+			@QueryParam("sort") @DefaultValue("date") final String sortBy, @QueryParam("fields") final String fields) {
 		final ResponseWrapper<List<ArticleFull>> res = new ResponseWrapper<>();
 
 		if (res.hasErrors())
@@ -106,6 +105,16 @@ public class ArticleResource {
 		}
 	}
 
+	@GET
+	@Produces(MediaType.APPLICATION_JSON)
+	@Path("{id}/similar")
+	public Response getSimilar(@PathParam("id") final String id, @QueryParam("skip") final Integer skip,
+			@QueryParam("limit") final Integer limit, @QueryParam("sort") @DefaultValue("date") final String sortBy,
+			@QueryParam("fields") final String fields) {
+		// TODO implement
+		return null;
+	}
+
 	@POST
 	@Consumes(MediaType.APPLICATION_JSON)
 	@Produces(MediaType.APPLICATION_JSON)
diff --git a/vipra-ui/app/html/about.html b/vipra-ui/app/html/about.html
index 15ba8404ab54343e0e96987d05728e16dacb07ab..bc1175ccd739d7b1d4ef483d20d2c69923e313b8 100644
--- a/vipra-ui/app/html/about.html
+++ b/vipra-ui/app/html/about.html
@@ -1,4 +1,4 @@
-<div ng-cloak ng-hide="$state.current.name !== 'about'">
+<div class="container" ng-cloak ng-hide="$state.current.name !== 'about'">
   <div class="page-header">
     <h1>About</h1>
   </div>
diff --git a/vipra-ui/app/html/articles/index.html b/vipra-ui/app/html/articles/index.html
index 85f4e3b1447fe2dc409edd3dda43a48e2029fb3d..32d5a302335f71278510da252b7414744139d990 100644
--- a/vipra-ui/app/html/articles/index.html
+++ b/vipra-ui/app/html/articles/index.html
@@ -1,4 +1,4 @@
-<div ng-cloak ng-hide="$state.current.name !== 'articles'">
+<div class="container" ng-cloak ng-hide="$state.current.name !== 'articles'">
   <div class="text-muted">
     Found
     <ng-pluralize count="articlesTotal||0" when="{0:'no articles',1:'1 article',other:'{} articles'}"></ng-pluralize> in the database.
diff --git a/vipra-ui/app/html/articles/show.html b/vipra-ui/app/html/articles/show.html
index d450d474e51a735a71f31c50764c7b86166fa32f..8f36ebc0ffbb625f7ab2c8bf6147da34546b863b 100644
--- a/vipra-ui/app/html/articles/show.html
+++ b/vipra-ui/app/html/articles/show.html
@@ -1,4 +1,4 @@
-<div ng-cloak ng-hide="$state.current.name !== 'articles.show'">
+<div class="container" ng-cloak ng-hide="$state.current.name !== 'articles.show'">
   <div class="page-header">
     <h1 ng-bind="::article.title"></h1>
     <table class="item-actions">
@@ -16,8 +16,8 @@
       </tr>
     </table>
   </div>
-  <h3>Info <hide-link target="#info"/></h3>
-  <div class="row" id="info">
+  <h3>Info</h3>
+  <div class="row">
     <div class="col-md-12">
       <table class="table table-bordered table-condensed table-fixed table-infos">
         <tbody>
@@ -54,8 +54,8 @@
       </table>
     </div>
   </div>
-  <h3>Topics <hide-link target="#topics"/></h3>
-  <div class="row" id="topics">
+  <h3>Topics</h3>
+  <div class="row">
     <div class="col-md-5">
       <table class="table table-morecondensed" ng-show="article.topics.length > 0">
         <tr>
diff --git a/vipra-ui/app/html/articles/similar.html b/vipra-ui/app/html/articles/similar.html
index dc2fb0618aa6e5cb7ffdbedf895d23cbbd63a2cc..a8d167b5e0d5e71a76f4f70851af6a673712029f 100644
--- a/vipra-ui/app/html/articles/similar.html
+++ b/vipra-ui/app/html/articles/similar.html
@@ -1,14 +1,16 @@
-<div ng-cloak ng-hide="$state.current.name !== 'articles.show.similar'">
+<div class="container" ng-cloak ng-hide="$state.current.name !== 'articles.show.similar'">
   <div class="page-header">
     <h1 ng-bind="::article.title"></h1>
     <table class="item-actions">
       <tr>
         <td>
-          <a class="btn btn-default" ui-sref="articles.show({id:article.id})">
-            Back
-          </a>
+          <a class="btn btn-default" ui-sref="^">Back</a>
         </td>
       </tr>
     </table>
   </div>
+  <h3>Similar articles</h3>
+  <div class="row">
+
+  </div>
 </div>
\ No newline at end of file
diff --git a/vipra-ui/app/html/directives/checkbox.html b/vipra-ui/app/html/directives/checkbox.html
deleted file mode 100644
index b951ed8d7d15c6926e025ea7ed839e7b5213f2f1..0000000000000000000000000000000000000000
--- a/vipra-ui/app/html/directives/checkbox.html
+++ /dev/null
@@ -1,2 +0,0 @@
-<input type="checkbox" ng-model="ngModel" style="display:none">
-<span class="glyphicon" ng-class="{'glyphicon-unchecked':!ngModel,'glyphicon-check':ngModel}" ng-click="ngModel=!ngModel"></span>
diff --git a/vipra-ui/app/html/explorer.html b/vipra-ui/app/html/explorer.html
new file mode 100644
index 0000000000000000000000000000000000000000..961e0f9d437afb9f7d99e5d334a393da120e53c3
--- /dev/null
+++ b/vipra-ui/app/html/explorer.html
@@ -0,0 +1,21 @@
+<div class="fullsize navpadding explorer" ng-cloak ng-hide="$state.current.name !== 'explorer'">
+  <div class="sidebar">
+    <button class="btn btn-sm btn-default" ng-click="checkTopics(true)">All</button>
+    <button class="btn btn-sm btn-default" ng-click="checkTopics(false)">None</button>
+    <button class="btn btn-sm btn-default" ng-click="checkTopics()">Toggle</button>
+    <ul class="list-unstyled topic-choice">
+      <li ng-repeat="topic in topics">
+        <div class="checkbox checkbox-condensed" ng-attr-title="{{::topic.name}}" ng-class="{selected:opts.selectedTopics[topic.id]}">
+          <input type="checkbox" ng-model="opts.selectedTopics[topic.id]" ng-attr-id="{{::topic.id}}" ng-model-store="selectedTopics-{{::topic.id}}">
+          <label class="check" ng-attr-for="{{::topic.id}}">
+            <span class="ellipsis" ng-bind="::topic.name"></span>
+          </label>
+          <span class="colorbox" style="background:rgb({{::topic.color.join()}})"></span>
+        </div>
+      </li>
+    </ul>
+  </div>
+  <div class="chart">
+    
+  </div>
+</div>
\ No newline at end of file
diff --git a/vipra-ui/app/html/index.html b/vipra-ui/app/html/index.html
index c95c3c01c88287372cdfb965b85a984281772b44..260825fac09599e9678a420d1a014b80e9eea49d 100644
--- a/vipra-ui/app/html/index.html
+++ b/vipra-ui/app/html/index.html
@@ -1,4 +1,4 @@
-<div ng-cloak ng-hide="$state.current.name !== 'index'">
+<div class="container" ng-cloak ng-hide="$state.current.name !== 'index'">
   <div class="row" ng-hide="search">
     <div class="col-md-12 text-center">
       <svg class="logo hover heading" viewBox="0 0 200 120">
diff --git a/vipra-ui/app/html/topics/articles.html b/vipra-ui/app/html/topics/articles.html
index a165d374ade675cc7087b9b91d80be9f6c20507c..a49f2a6e0780007561b08a8fa5d9b226dfb1a327 100644
--- a/vipra-ui/app/html/topics/articles.html
+++ b/vipra-ui/app/html/topics/articles.html
@@ -1,4 +1,4 @@
-<div ng-cloak ng-hide="$state.current.name !== 'topics.show.articles'">
+<div class="container" ng-cloak ng-hide="$state.current.name !== 'topics.show.articles'">
   <div class="page-header">
     <h1 ng-bind-template="Articles for topic '{{::topic.name}}'"></h1>
     <table class="item-actions">
diff --git a/vipra-ui/app/html/topics/index.html b/vipra-ui/app/html/topics/index.html
index c181b8328d97b3c75929bf631a4f8e113204070a..ea87c18abf792c9436fc20672e49798d4f486346 100644
--- a/vipra-ui/app/html/topics/index.html
+++ b/vipra-ui/app/html/topics/index.html
@@ -1,4 +1,4 @@
-<div ng-cloak ng-hide="$state.current.name !== 'topics'">
+<div class="container" ng-cloak ng-hide="$state.current.name !== 'topics'">
   <div class="text-muted">
     Found
     <ng-pluralize count="topicsTotal||0" when="{0:'no topics',1:'1 topic',other:'{} topics'}"></ng-pluralize> in the database.
diff --git a/vipra-ui/app/html/topics/show.html b/vipra-ui/app/html/topics/show.html
index 292542a1cf3a4962482b16fb73216069ed77654c..6dd31fad7b18a8ce006f64d88a0e3fa1ed7c7ced 100644
--- a/vipra-ui/app/html/topics/show.html
+++ b/vipra-ui/app/html/topics/show.html
@@ -1,4 +1,4 @@
-<div ng-cloak ng-hide="$state.current.name !== 'topics.show'">
+<div class="container" ng-cloak ng-hide="$state.current.name !== 'topics.show'">
   <div class="page-header">
     <h1>
       <div ng-bind="topic.name" ng-hide="isRename"></div>
@@ -33,8 +33,8 @@
       </tr>
     </table>
   </div>
-  <h3>Info <hide-link target="#info"/></h3>
-  <div class="row" id="info">
+  <h3>Info</h3>
+  <div class="row">
     <div class="col-md-12">
       <table class="table table-bordered table-condensed table-fixed table-infos">
         <tbody>
@@ -55,22 +55,22 @@
     </div>
   </div>
   <div ng-if="!topic.dynamic">
-    <h3>Words <hide-link target="#words"/></h3>
-    <div class="row" id="words">
+    <h3>Words</h3>
+    <div class="row">
       <div class="col-md-12">
         <table class="table table-bordered table-condensed table-fixed">
           <thead>
             <tr>
-              <th sort-by="id" sort-type="wordSort" sort-reverse="wordSortRev">
+              <th sort-by="id" ng-model="opts.wordSort" sort-reverse="opts.wordSortRev">
                 Word
               </th>
-              <th sort-by="likeliness" sort-type="wordSort" sort-reverse="wordSortRev">
+              <th sort-by="likeliness" ng-model="opts.wordSort" sort-reverse="opts.wordSortRev">
                 Likeliness
               </th>
             </tr>
           </thead>
           <tbody>
-            <tr ng-repeat="word in topic.words | orderBy:wordSort:wordSortRev">
+            <tr ng-repeat="word in topic.words | orderBy:opts.wordSort:opts.wordSortRev">
               <td>
                 <a ui-sref="words.show({id:word.id})" ng-bind="word.id"></a>
               </td>
@@ -82,27 +82,25 @@
     </div>
   </div>
   <div ng-if="topic.dynamic">
-    <h3>Relevance over time <hide-link target="#relevance"/></h3>
-    <div id="relevance">
-      <div class="row">
-        <div class="col-md-12">
-          <div class="well well-sm">
-            <label class="radio-inline">
-              <input type="radio" name="seqstyle" ng-model="seqstyle" value="absolute" ng-model-store="seqStyle" ng-model-default="'absolute'">Absolute
-            </label>
-            <label class="radio-inline">
-              <input type="radio" name="seqstyle" ng-model="seqstyle" value="relative" ng-model-store="seqStyle">Relative
-            </label>
-          </div>
+    <h3>Relevance over time</h3>
+    <div class="row">
+      <div class="col-md-12">
+        <div class="well well-sm">
+          <label class="radio-inline">
+            <input type="radio" ng-model="opts.seqstyle" value="absolute" ng-model-store="seqStyle" ng-model-default="'absolute'" ng-change="changeSeqStyle()">Absolute
+          </label>
+          <label class="radio-inline">
+            <input type="radio" ng-model="opts.seqstyle" value="relative" ng-model-store="seqStyle" ng-change="changeSeqStyle()">Relative
+          </label>
         </div>
       </div>
-      <br>
-      <div class="row">
-        <div class="col-md-12">
-          <div class="area-chart" id="topic-seq" highcharts="topicSeq"></div>
-        </div>
+    </div>
+    <br>
+    <div class="row">
+      <div class="col-md-12">
+        <div class="area-chart" id="topic-seq" highcharts="topicSeq"></div>
       </div>
-    </div> 
-  </div>
+    </div>
+  </div> 
 </div>
 <div ng-cloak ui-view></div>
diff --git a/vipra-ui/app/index.html b/vipra-ui/app/index.html
index 666f2d800f038fddf0dcca03b9398098f633b6c8..002b44c155d9b120542d7e82ebe174b131e0ac72 100644
--- a/vipra-ui/app/index.html
+++ b/vipra-ui/app/index.html
@@ -34,7 +34,7 @@
 </head>
 
 <body>
-  <nav class="navbar navbar-default navbar-static-top navbar-breadcrumbs">
+  <nav class="navbar navbar-default navbar-static-top">
     <div class="container-fluid">
       <!-- Brand and toggle get grouped for better mobile display -->
       <div class="navbar-header">
@@ -56,10 +56,13 @@
       <!-- Collect the nav links, forms, and other content for toggling -->
       <div class="collapse navbar-collapse" id="vipra-navbar-collapse-1">
         <ul class="nav navbar-nav">
-          <li ng-class="{active:$state.includes('articles')}">
+          <li ui-sref-active="active">
+            <a ui-sref="explorer">Explorer</a>
+          </li>
+          <li ui-sref-active="active">
             <a ui-sref="articles">Articles</a>
           </li>
-          <li ng-class="{active:$state.includes('topics')}">
+          <li ui-sref-active="active">
             <a ui-sref="topics">Topics</a>
           </li>
         </ul>
@@ -75,8 +78,7 @@
     </div>
     <!-- /.container-fluid -->
   </nav>
-  <div ncy-breadcrumb ng-hide="$state.current.name === 'index'"></div>
-  <div class="container" ui-view ng-cloak></div>
+  <div class="main" ui-view ng-cloak></div>
 </body>
 
 </html>
diff --git a/vipra-ui/app/js/app.js b/vipra-ui/app/js/app.js
index fe5fbd1c5e6ea5f7426b62594ef2b970b6ba7101..515882f4e578d6bc16fc36a1554b5a5da97d825b 100644
--- a/vipra-ui/app/js/app.js
+++ b/vipra-ui/app/js/app.js
@@ -2,7 +2,7 @@
  * Vipra Application
  * Main application file
  ******************************************************************************/
-/* globals angular, console, NProgress */
+/* globals angular */
 (function() {
 
   "use strict";
@@ -10,10 +10,8 @@
   var app = angular.module('vipra.app', [
     'ngResource',
     'ngSanitize',
-    'ngWebSocket',
     'ui.router',
     'nya.bootstrap.select',
-    'ncy-angular-breadcrumb',
     'vipra.controllers',
     'vipra.directives',
     'vipra.factories',
@@ -30,28 +28,25 @@
     $stateProvider.state('index', {
       url: '/',
       templateUrl: 'html/index.html',
-      controller: 'IndexController',
-      ncyBreadcrumb: {
-        label: 'Start'
-      }
+      controller: 'IndexController'
     });
 
     $stateProvider.state('about', {
       url: '/about',
       templateUrl: 'html/about.html',
-      controller: 'AboutController',
-      ncyBreadcrumb: {
-        label: 'About'
-      }
+      controller: 'AboutController'
     });
 
     $stateProvider.state('network', {
       url: '/network/:type/:id',
       templateUrl: 'html/network.html',
-      controller: 'NetworkController',
-      ncyBreadcrumb: {
-        label: 'Network'
-      }
+      controller: 'NetworkController'
+    });
+
+    $stateProvider.state('explorer', {
+      url: '/explorer',
+      templateUrl: 'html/explorer.html',
+      controller: 'ExplorerController'
     });
 
     // states: articles
@@ -59,28 +54,19 @@
     $stateProvider.state('articles', {
       url: '/articles',
       templateUrl: 'html/articles/index.html',
-      controller: 'ArticlesIndexController',
-      ncyBreadcrumb: {
-        label: 'Articles'
-      }
+      controller: 'ArticlesIndexController'
     });
 
     $stateProvider.state('articles.show', {
       url: '/:id',
       templateUrl: 'html/articles/show.html',
-      controller: 'ArticlesShowController',
-      ncyBreadcrumb: {
-        label: '{{article.title.ellipsize(30)}}'
-      }
+      controller: 'ArticlesShowController'
     });
 
     $stateProvider.state('articles.show.similar', {
       url: '/similar',
       templateUrl: 'html/articles/similar.html',
-      controller: 'ArticlesSimilarController',
-      ncyBreadcrumb: {
-        label: 'Similar'
-      }
+      controller: 'ArticlesSimilarController'
     });
 
     // states: topics
@@ -88,34 +74,23 @@
     $stateProvider.state('topics', {
       url: '/topics',
       templateUrl: 'html/topics/index.html',
-      controller: 'TopicsIndexController',
-      ncyBreadcrumb: {
-        label: 'Topics'
-      }
+      controller: 'TopicsIndexController'
     });
 
     $stateProvider.state('topics.show', {
       url: '/:id',
       templateUrl: 'html/topics/show.html',
-      controller: 'TopicsShowController',
-      ncyBreadcrumb: {
-        label: '{{topic.name}}'
-      }
+      controller: 'TopicsShowController'
     });
 
     $stateProvider.state('topics.show.articles', {
       url: '/articles',
       templateUrl: 'html/topics/articles.html',
-      controller: 'TopicsArticlesController',
-      ncyBreadcrumb: {
-        label: 'Articles'
-      }
+      controller: 'TopicsArticlesController'
     });
 
   }]);
 
-  var loadingTimeout;
-
   app.config(['$httpProvider', function($httpProvider) {
 
     $httpProvider.interceptors.push(function($q, $injector, $rootScope) {
@@ -123,19 +98,12 @@
         $rootScope.loading.requests = ++$rootScope.loading.requests || 1;
         $rootScope.loading[config.method] = ++$rootScope.loading[config.method] || 1;
         $rootScope.loading.any = true;
-
-        clearTimeout(loadingTimeout);
       };
 
       var requestDecrement = function(config) {
         $rootScope.loading.requests = Math.max(--$rootScope.loading.requests, 0);
         $rootScope.loading[config.method] = Math.max(--$rootScope.loading[config.method], 0);
         $rootScope.loading.any = $rootScope.loading.requests > 0;
-
-        loadingTimeout = setTimeout(function() {
-          console.log('loading done');
-          NProgress.done();
-        }, 100);
       };
 
       return {
@@ -167,16 +135,6 @@
 
     $rootScope.loading = {};
 
-    $rootScope.$on('$stateChangeStart', function(event, toState) {
-      console.log('changing state: ' + toState.name);
-      NProgress.start();
-
-      loadingTimeout = setTimeout(function() {
-        console.log('loading done');
-        NProgress.done();
-      }, 100);
-    });
-
   }]);
 
 })();
diff --git a/vipra-ui/app/js/controllers.js b/vipra-ui/app/js/controllers.js
index 0658190d7980963ce5c09151d6f80b63ca14010c..bf42c96a245e1d81dd22bd94502f1282ccba027d 100644
--- a/vipra-ui/app/js/controllers.js
+++ b/vipra-ui/app/js/controllers.js
@@ -290,6 +290,36 @@
     }
   ]);
 
+  app.controller('ExplorerController', ['$scope', 'TopicFactory',
+    function($scope, TopicFactory) {
+
+      $scope.opts = {};
+
+      TopicFactory.query({}, function(data) {
+        $scope.topics = data;
+        var colors = Vipra.generateColors($scope.topics.length);
+        for (var i = 0; i < $scope.topics.length; i++)
+          $scope.topics[i].color = colors[i];
+      }, function(err) {
+        $scope.errors = err;
+      });
+
+      $scope.checkTopics = function(to) {
+        var toggle = typeof to === 'undefined';
+        if(!$scope.opts.selectedTopics)
+          $scope.opts.selectedTopics = {};
+        for(var i = 0, t; i < $scope.topics.length; i++) {
+          t = $scope.topics[i];
+          if(toggle)
+            $scope.opts.selectedTopics[t.id] = !$scope.opts.selectedTopics[t.id];
+          else
+            $scope.opts.selectedTopics[t.id] = to;
+        }
+      };
+
+    }
+  ]);
+
   /****************************************************************************
    * Article Controllers
    ****************************************************************************/
@@ -371,6 +401,23 @@
     }
   ]);
 
+  /**
+   * Article Similar route
+   */
+  app.controller('ArticlesSimilarController', ['$scope', '$stateParams', 'ArticleFactory',
+    function($scope, $stateParams, ArticleFactory) {
+
+      ArticleFactory.get({
+        id: $stateParams.id
+      }, function(data) {
+        $scope.articles = data;
+      }, function(err) {
+        $scope.errors = err;
+      });
+
+    }
+  ]);
+
   /****************************************************************************
    * Topic Controllers
    ****************************************************************************/
@@ -409,8 +456,10 @@
   app.controller('TopicsShowController', ['$scope', '$stateParams', '$timeout', 'TopicFactory',
     function($scope, $stateParams, $timeout, TopicFactory) {
 
-      $scope.wordSort = $scope.wordSort || 'likeliness';
-      $scope.wordSortRev = typeof $scope.wordSortRev === 'undefined' ? true : $scope.wordSortRev;
+      $scope.opts = {
+        wordSort: $scope.wordSort || 'likeliness',
+        wordSortRev: typeof $scope.wordSortRev === 'undefined' ? true : $scope.wordSortRev
+      };
 
       TopicFactory.get({
         id: $stateParams.id
@@ -418,36 +467,6 @@
         $scope.topic = data;
         $scope.topicCreated = Vipra.formatDateTime($scope.topic.created);
         $scope.topicModified = Vipra.formatDateTime($scope.topic.modified);
-
-        if($scope.topic.dynamic) {
-          var relevances = [];
-          for(var i = 0, s; i < $scope.topic.sequences.length; i++) {
-            s = $scope.topic.sequences[i];
-            relevances.push([new Date(s.window.startDate).getTime(), s.relevance]);
-          }
-          $scope.topicSeq = {
-            chart: {
-              type: 'areaspline',
-              zoomType: 'x',
-              spacingLeft: 0,
-              spacingRight: 0
-            },
-            title: { text: '' },
-            xAxis: { 
-              type: 'datetime', 
-              title: { text: '' }
-            },
-            yAxis: { title: { text: 'Relevance' } },
-            tooltip: {
-              headerFormat: '',
-              pointFormat: '{point.x:%Y}: {point.y:.4f}'
-            },
-            legend: { enabled: false },
-            credits: { enabled: false },
-            plotOptions: { areaspline: { fillOpacity: 0.5 } },
-            series: [ { name: $scope.topic.name, data: relevances } ]
-          };
-        }
       }, function(err) {
         $scope.errors = err;
       });
@@ -466,8 +485,6 @@
           TopicFactory.update({ id: $scope.topic.id }, $scope.topic, function(data) {
             $scope.topic = data;
             $scope.isRename = false;
-          }, function(errors) {
-            $scope.renameErrors = Vipra.getErrors(errors);
           }, function(err) {
             $scope.errors = err;
           });
@@ -483,6 +500,60 @@
           $event.preventDefault();
         }
       };
+
+      $scope.$watch('opts.seqstyle', function(style) {
+        if (!style) return;
+        console.log('redraw relevance chart');
+        var min = 0,
+          max = 0,
+          relevances = [];
+        // create series
+        for (var i = 0, sequence, relevance; i < $scope.topic.sequences.length; i++) {
+          sequence = $scope.topic.sequences[i];
+          relevance = $scope.opts.seqstyle === 'relative' ? sequence.relevanceChange : sequence.relevance;
+          relevances.push([new Date(sequence.window.startDate).getTime(), relevance]);
+          if (relevances[i][1] > relevances[max][1])
+            max = i;
+          else if (relevances[i][1] < relevances[min][1])
+            min = i;
+        }
+
+        // mark maximum and minimum value
+        relevances[max] = {
+          x: relevances[max][0],
+          y: relevances[max][1],
+          marker: { symbol: 'triangle', fillColor: '#f00' }
+        };
+        relevances[min] = {
+          x: relevances[min][0],
+          y: relevances[min][1],
+          marker: { symbol: 'triangle-down', fillColor: '#f00' }
+        };
+
+        // highcharts configuration
+        $scope.topicSeq = {
+          chart: {
+            type: 'areaspline',
+            zoomType: 'x',
+            spacingLeft: 0,
+            spacingRight: 0
+          },
+          title: { text: '' },
+          xAxis: {
+            type: 'datetime',
+            title: { text: '' }
+          },
+          yAxis: { title: { text: 'Relevance' } },
+          tooltip: {
+            headerFormat: '',
+            pointFormat: '{point.x:%Y}: {point.y:.4f}'
+          },
+          legend: { enabled: false },
+          credits: { enabled: false },
+          plotOptions: { areaspline: { fillOpacity: 0.5 } },
+          series: [{ name: $scope.topic.name, data: relevances }]
+        };
+      });
     }
   ]);
 
diff --git a/vipra-ui/app/js/directives.js b/vipra-ui/app/js/directives.js
index 23f4deaeb77601021f90269cf859b063161b01f1..d790879ba8a00a2fbd85f584fb80f8ac96b4fbf4 100644
--- a/vipra-ui/app/js/directives.js
+++ b/vipra-ui/app/js/directives.js
@@ -65,43 +65,6 @@
     };
   });
 
-  app.directive('hideLink', ['$state', 'Store', function($state, Store) {
-    return {
-      scope: {
-        target: '@',
-        store: '@'
-      },
-      replace: true,
-      template: '<a class="hide-link" href="#" ng-bind="text" ng-click="change($event)"></a>',
-      link: function($scope) {
-        var target = $($scope.target);
-        if (!target.length) return;
-        var store = $scope.store !== 'false';
-        var name = 'hidelink-' + $scope.target;
-        if (store) {
-          var visible = Store(name);
-          if (visible !== null && typeof visible !== 'undefined')
-            target.toggle(visible);
-        }
-
-        var setText = function(b) {
-          $scope.text = b ? 'hide' : 'show';
-        };
-
-        $scope.change = function(e) {
-          e.preventDefault();
-          var visible = !target.is(':visible');
-          setText(visible);
-          target.slideToggle();
-          if (store)
-            Store(name, visible);
-        };
-
-        setText(target.is(':visible'));
-      }
-    };
-  }]);
-
   app.directive('toggleLink', function() {
     return {
       link: function($scope, $elem, $attrs) {
@@ -117,11 +80,16 @@
       restrict: 'A',
       require: 'ngModel',
       link: function($scope, $elem, $attrs, $ctrl) {
-        if (!$attrs.ngModelStore) {
-          console.log("no store key given");
+        if (!$attrs.ngModel) {
+          console.log('no model given');
+          return;
+        }
+        if (typeof $attrs.ngModelStore === 'undefined') {
+          console.log('no store key given');
           return;
         }
-        var value = Store($attrs.ngModelStore);
+        var key = $attrs.ngModelStore;
+        var value = Store(key);
         if (value !== null) {
           $ctrl.$setViewValue(value);
           $ctrl.$render();
@@ -129,8 +97,8 @@
           $ctrl.$setViewValue($scope.$eval($attrs.ngModelDefault));
           $ctrl.$render();
         }
-        $ctrl.$viewChangeListeners.push(function() {
-          Store($attrs.ngModelStore, $ctrl.$viewValue);
+        $scope.$watch($attrs.ngModel, function() {
+          Store(key, $ctrl.$viewValue);
         });
       }
     };
@@ -179,27 +147,17 @@
     };
   });
 
-  app.directive('bsCheckbox', function() {
-    return {
-      scope: {
-        ngModel: '='
-      },
-      restrict: 'E',
-      templateUrl: 'html/directives/checkbox.html'
-    };
-  });
-
   app.directive('sortBy', ['Store', function(Store) {
     return {
       scope: {
         sortBy: '@',
-        sortType: '=',
+        ngModel: '=',
         sortReverse: '=',
-        storeKey: '@sortType'
+        storeKey: '@ngModel'
       },
       restrict: 'A',
       transclude: true,
-      template: '<span ng-click="click()"><ng-transclude/><span ng-show="sortType == sortBy" class="caret" ng-class="{\'caret-up\':sortReverse}"></span></span>',
+      template: '<span ng-click="click()"><ng-transclude/><span ng-show="ngModel == sortBy" class="caret" ng-class="{\'caret-up\':sortReverse}"></span></span>',
       link: function($scope, $elem, $attrs) {
         if (!$attrs.sortBy) {
           console.log('no sort by key given');
@@ -208,14 +166,14 @@
         var value = Store($scope.storeKey);
         if (value) {
           value = value.split(',');
-          $scope.sortType = value[0];
+          $scope.ngModel = value[0];
           $scope.sortReverse = value[1] === 'true';
         }
         $scope.sortBy = $attrs.sortBy;
         $scope.click = function() {
-          $scope.sortType = $scope.sortBy;
+          $scope.ngModel = $scope.sortBy;
           $scope.sortReverse = !$scope.sortReverse;
-          Store($scope.storeKey, $scope.sortType + ',' + $scope.sortReverse);
+          Store($scope.storeKey, $scope.ngModel + ',' + $scope.sortReverse);
         };
       }
     };
diff --git a/vipra-ui/app/js/factories.js b/vipra-ui/app/js/factories.js
index 0e754c75690eeb63b5369dd14174f1fa182e3177..b745096610a111a512ce565689efbb72070ce12d 100644
--- a/vipra-ui/app/js/factories.js
+++ b/vipra-ui/app/js/factories.js
@@ -10,7 +10,9 @@
   var app = angular.module('vipra.factories', []);
 
   app.factory('ArticleFactory', ['$resource', function($resource) {
-    return $resource(Vipra.config.restUrl + '/articles/:id');
+    return $resource(Vipra.config.restUrl + '/articles/:id', {}, {
+      similar: { isArray: true, url: Vipra.config.restUrl + '/articles/:id/similar' }
+    });
   }]);
 
   app.factory('TopicFactory', ['$resource', function($resource) {
@@ -103,25 +105,4 @@
     };
   }]);
 
-  app.service('WebSocketService', ['$websocket', '$state', function($websocket, $state) {
-    var socket = $websocket(Vipra.config.websocketUrl),
-      callback = null;
-
-    socket.onMessage(function(message) {
-      var data = JSON.parse(message.data);
-      if (callback)
-        callback(data);
-    });
-
-    this.send = function(type, data) {
-      socket.send(JSON.stringify({ type: type, data: JSON.stringify(data) }));
-    };
-
-    this.receive = function(fn) {
-      callback = fn;
-    };
-
-    this.send(1, { state: $state.current.name });
-  }]);
-
 })();
diff --git a/vipra-ui/app/js/helpers.js b/vipra-ui/app/js/helpers.js
index b8362ea044ce896522db612c6c6b92932c7c69bc..0898a924c40c7fe542c34652a2c0ee64bcd96996 100644
--- a/vipra-ui/app/js/helpers.js
+++ b/vipra-ui/app/js/helpers.js
@@ -36,13 +36,96 @@
     return 'id' + Math.random().toString(36).substring(7);
   };
 
-  Vipra.getErrors = function(errors) {
-    var html = [];
-    if (errors && errors.length) {
-      for (var i = 0; i < errors.length; i++)
-        html.push('<strong>' + errors[i].title + '</strong>: ' + errors[i].detail);
+  /**
+   * taken from
+   * https://jsfiddle.net/aldass/9f0yadfy/
+   */
+  Vipra.generateColors = function(t) {
+    t = parseInt(t);
+    if (t < 2)
+      throw new Error("'t' must be greater than 1.");
+
+    // distribute the colors evenly on
+    // the hue range (the 'H' in HSV)
+    var i = 360 / (t - 1);
+
+    // hold the generated colors
+    var r = [];
+    var sv = 70;
+    for (var x = 0; x < t; x++) {
+      // alternate the s, v for more
+      // contrast between the colors.
+      sv = sv > 90 ? 70 : sv + 10;
+      r.push(Vipra.hsvToRgb(i * x, sv, sv));
     }
-    return html.join('<br>');
+    return r;
+  };
+
+  /**
+   * HSV to RGB color conversion
+   *
+   * H runs from 0 to 360 degrees
+   * S and V run from 0 to 100
+   * 
+   * Ported from the excellent java algorithm by Eugene Vishnevsky at:
+   * http://www.cs.rit.edu/~ncs/color/t_convert.html
+   */
+  Vipra.hsvToRgb = function(h, s, v) {
+    var r, g, b;
+    var i;
+    var f, p, q, t;
+
+    h = Math.max(0, Math.min(360, h));
+    s = Math.max(0, Math.min(100, s));
+    v = Math.max(0, Math.min(100, v));
+    s /= 100;
+    v /= 100;
+
+    if (s === 0) {
+      r = g = b = v;
+      return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
+    }
+
+    h /= 60;
+    i = Math.floor(h);
+    f = h - i;
+    p = v * (1 - s);
+    q = v * (1 - s * f);
+    t = v * (1 - s * (1 - f));
+
+    switch (i) {
+      case 0:
+        r = v;
+        g = t;
+        b = p;
+        break;
+      case 1:
+        r = q;
+        g = v;
+        b = p;
+        break;
+      case 2:
+        r = p;
+        g = v;
+        b = t;
+        break;
+      case 3:
+        r = p;
+        g = q;
+        b = v;
+        break;
+      case 4:
+        r = t;
+        g = p;
+        b = v;
+        break;
+      default:
+        r = v;
+        g = p;
+        b = q;
+    }
+
+    return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
   };
 
   /**
diff --git a/vipra-ui/app/less/app.less b/vipra-ui/app/less/app.less
index 9c8729b4ab4187641c7c46c7c384aa77b5440e4c..3613090040c9e81e772571fb81fba09265b60ceb 100644
--- a/vipra-ui/app/less/app.less
+++ b/vipra-ui/app/less/app.less
@@ -106,7 +106,7 @@ ul.dashed {
 
 .graph-legend {
   position: absolute;
-  top: 97px;
+  top: 60px;
   left: 10px;
   font-weight: bold;
   padding: 10px;
@@ -132,10 +132,6 @@ ul.dashed {
   display: block;
 }
 
-.hide-link {
-  font-size: 12px;
-}
-
 .noselect {
   -webkit-touch-callout: none;
   -webkit-user-select: none;
@@ -154,7 +150,7 @@ ul.dashed {
 }
 
 .navpadding {
-  padding-top: 87px;
+  padding-top: 51px;
 }
 
 .form-control-inline {
@@ -225,10 +221,6 @@ ul.dashed {
   margin-top: -2px;
 }
 
-.navbar-breadcrumbs {
-  margin-bottom: 0;
-}
-
 .topic-share {
   width: 75px;
 }
@@ -247,10 +239,6 @@ revolve-select, [revolve-select] {
   display: none;
 }
 
-bs-checkbox {
-  cursor: pointer;
-}
-
 .logo {
   &.hover {
     .logo-shape {
@@ -270,6 +258,78 @@ bs-checkbox {
   }
 }
 
+.vis-network {
+  outline: none;
+}
+
+.fullheight {
+  height: 100%;
+
+  .row > * {
+    height: 100%;
+  }
+}
+
+.checkbox-condensed {
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
+.ellipsis {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  display: inline-block;
+  width: 100%;
+  vertical-align: middle;
+}
+
+.explorer {
+  display: flex;
+  flex-direction: row;
+
+  .sidebar {
+    flex: 0 0 250px;
+    width: 250px;
+    background: #f9f9f9;
+    height: 100%;
+    padding: 5px 0 5px 5px;
+  }
+
+  .chart {
+    flex: 1 0 0;
+  }
+
+  .topic-choice {
+    padding-top: 5px;
+
+    label {
+      padding-right: 15px;
+    }
+  }
+
+  .colorbox {
+    display: inline-block;
+    visibility: hidden;
+    width: 10px;
+    height: 20px;
+    vertical-align: middle;
+    position: absolute;
+    right: 0;
+    top: 0;
+  }
+
+  .checkbox:hover .colorbox,
+  .selected .colorbox {
+    visibility: visible;
+  }
+}
+
+input[type=checkbox],
+.checkbox label::before {
+  cursor: pointer;
+}
+
 @-moz-keyframes spin { 100% { -moz-transform: rotateY(360deg); } }
 @-webkit-keyframes spin { 100% { -webkit-transform: rotateY(360deg); } }
 @keyframes spin { 100% { -webkit-transform: rotateY(360deg); transform:rotateY(360deg); } }
diff --git a/vipra-ui/bower.json b/vipra-ui/bower.json
index d1495f65d49fb7395fcbf9fad62dcccbbcf10930..c484899bfc46e5503535d8c4ab4646278594e2e6 100644
--- a/vipra-ui/bower.json
+++ b/vipra-ui/bower.json
@@ -17,18 +17,17 @@
     "tests"
   ],
   "dependencies": {
-    "bootstrap": "~3.3.6",
-    "jquery": "^2.2.0",
-    "angular": "^1.5.0",
-    "angular-resource": "^1.5.0",
+    "bootstrap": "~3.x",
+    "jquery": "^2.x",
+    "angular": "^1.x",
+    "angular-resource": "^1.x",
     "angular-ui-router": "^0.2.17",
-    "angular-sanitize": "^1.5.0",
-    "highcharts": "^4.2.2",
-    "vis": "https://github.com/almende/vis.git#^4.14.0",
-    "angular-websocket": "^1.0.14",
-    "moment": "^2.11.2",
-    "nya-bootstrap-select": "^2.1.3",
-    "nprogress": "^0.2.0",
-    "angular-breadcrumb": "^0.4.1"
+    "angular-sanitize": "^1.x",
+    "highcharts": "^4.x",
+    "vis": "^4.x",
+    "moment": "^2.x",
+    "nya-bootstrap-select": "^2.x",
+    "font-awesome": "^4.x",
+    "awesome-bootstrap-checkbox": "^0.3.7"
   }
 }
diff --git a/vipra-ui/gulpfile.js b/vipra-ui/gulpfile.js
index 56965ffd942a7b332f016a201512e6c25a05c3fd..3cfe64cac2daa164bce78064dfa23023e01ff701 100644
--- a/vipra-ui/gulpfile.js
+++ b/vipra-ui/gulpfile.js
@@ -17,23 +17,22 @@ var assets = {
     'bower_components/angular-resource/angular-resource.min.js',
     'bower_components/angular-sanitize/angular-sanitize.min.js',
     'bower_components/angular-ui-router/release/angular-ui-router.min.js',
-    'bower_components/angular-websocket/angular-websocket.min.js',
-    'bower_components/angular-breadcrumb/dist/angular-breadcrumb.min.js',
     'bower_components/bootstrap/dist/js/bootstrap.min.js',
     'bower_components/highcharts/highcharts.js',
     'bower_components/vis/dist/vis.min.js',
-    'bower_components/nprogress/nprogress.js',
     'bower_components/moment/min/moment.min.js',
     'bower_components/nya-bootstrap-select/dist/js/nya-bs-select.min.js'
   ],
   css: [
     'bower_components/bootstrap/dist/css/bootstrap.min.css',
+    'bower_components/font-awesome/css/font-awesome.min.css',
     'bower_components/vis/dist/vis.min.css',
-    'bower_components/nprogress/nprogress.css',
-    'bower_components/nya-bootstrap-select/dist/css/nya-bs-select.min.css'
+    'bower_components/nya-bootstrap-select/dist/css/nya-bs-select.min.css',
+    'bower_components/awesome-bootstrap-checkbox/awesome-bootstrap-checkbox.css'
   ],
   fonts: [
-    'bower_components/bootstrap/dist/fonts/*'
+    'bower_components/bootstrap/dist/fonts/*',
+    'bower_components/font-awesome/fonts/*'
   ],
   img: []
 };
@@ -50,11 +49,11 @@ gulp.task('less', function() {
 
 gulp.task('js', function() {
   gulp.src('app/js/**/*.js')
-      .pipe(sourcemaps.init())
+      //.pipe(sourcemaps.init())
       .pipe(concat('app.js'))
       .pipe(ngannotate())
-      .pipe(uglify())
-      .pipe(sourcemaps.write('.'))
+      //.pipe(uglify())
+      //.pipe(sourcemaps.write('.'))
       .pipe(gulp.dest('public/js'));
 });