diff --git a/src/components/experiment-list/experiment-list-element.css b/src/components/experiment-list/experiment-list-element.css
index 7383c3a3efe8d0d7d59ffe9514a8a9048f950a9e..8ce8b3aa4cfec6c89bd6a5cf73575b243461ab9f 100644
--- a/src/components/experiment-list/experiment-list-element.css
+++ b/src/components/experiment-list/experiment-list-element.css
@@ -113,4 +113,15 @@
 
 .server-status-restricted {
   background-color: #f0ad4e;
+}
+
+.h4 {
+  font-size: 1.2em;
+  font-weight: bold;
+  padding-bottom: 10px;
+}
+
+.exp-title-sim-info {
+  padding-left: 20px;
+  font-style: italic;
 }
\ No newline at end of file
diff --git a/src/components/experiment-list/experiment-list-element.js b/src/components/experiment-list/experiment-list-element.js
index 1734b1caae2ca8b03c2cd751d30a2bdf5624f4af..907d5af87ac91816090a1dea720b4b524767fd06 100644
--- a/src/components/experiment-list/experiment-list-element.js
+++ b/src/components/experiment-list/experiment-list-element.js
@@ -1,7 +1,7 @@
 import React from 'react';
 import timeDDHHMMSS from '../../utility/time-filter.js';
 import ExperimentStorageService from '../../services/experiments/storage/experiment-storage-service.js';
-import ExperimentServerService from '../../services/experiments/execution/experiment-server-service.js';
+import ExperimentServerService from '../../services/experiments/execution/server-resources-service.js';
 import ExperimentExecutionService from '../../services/experiments/execution/experiment-execution-service.js';
 
 import SimulationDetails from './simulation-details';
@@ -131,7 +131,11 @@ export default class ExperimentListElement extends React.Component {
             <div className='h4'>
               {exp.configuration.name}
             </div>
-            <br />
+            {exp.joinableServers.length > 0 ?
+              <div className='exp-title-sim-info'>
+                ({exp.joinableServers.length} simulation{exp.joinableServers.length > 1 ? 's' : ''} running)
+              </div>
+              : null}
           </div>
           <div>
             {!this.state.selected && exp.configuration.description.length > SHORT_DESCRIPTION_LENGTH ?
diff --git a/src/components/experiment-list/simulation-details.js b/src/components/experiment-list/simulation-details.js
index 10b3771432030f8f50f8863e0263c3f0028e196b..98fbd4a6ca85151cdd2c565c090d33d2ac742ce7 100644
--- a/src/components/experiment-list/simulation-details.js
+++ b/src/components/experiment-list/simulation-details.js
@@ -73,18 +73,17 @@ export default class SimulationDetails extends React.Component {
               <div>{simulation.runningSimulation.state}</div>
               <div>
                 {/* Join button enabled provided simulation state is consistent */}
-                <button analytics-on analytics-event="Join" analytics-category="Experiment"
+                <button /*analytics-on analytics-event="Join" analytics-category="Experiment"
                   ng-click="(simulation.runningSimulation.state === STATE.CREATED) ||
-                    simulation.stopping || joinExperiment(simulation, exp);"
+                    simulation.stopping || joinExperiment(simulation, exp);"*/
                   type="button" className="btn btn-default"
                   disabled={this.isJoinDisabled(simulation)}>
                   Join »
                 </button>
                 {/* Stop button enabled provided simulation state is consistent */}
-                <button analytics-on analytics-event="Stop" analytics-category="Experiment"
+                <button /*analytics-on analytics-event="Stop" analytics-category="Experiment"*/
                   onClick={() => ExperimentExecutionService.instance.stopExperiment(simulation)}
                   type="button" className="btn btn-default"
-                  ng-if="canStopSimulation(simulation)"
                   disabled={this.isStopDisabled(simulation)}
                   title={this.state.titleButtonStop}>
                   <i className="fa fa-spinner fa-spin" ng-if="simulation.stopping"></i> Stop
diff --git a/src/components/experiment-overview/experiment-overview.js b/src/components/experiment-overview/experiment-overview.js
index e622a67ac0075873e81b44e61f9787c7540012ad..6b67ad2b22f13efd8520d3744fc8fb24c571d2ea 100644
--- a/src/components/experiment-overview/experiment-overview.js
+++ b/src/components/experiment-overview/experiment-overview.js
@@ -3,7 +3,7 @@ import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
 import 'react-tabs/style/react-tabs.css';
 
 import ExperimentStorageService from '../../services/experiments/storage/experiment-storage-service.js';
-import ExperimentServerService from '../../services/experiments/execution/experiment-server-service.js';
+import ExperimentServerService from '../../services/experiments/execution/server-resources-service.js';
 import ExperimentExecutionService from '../../services/experiments/execution/experiment-execution-service.js';
 
 import ExperimentList from '../experiment-list/experiment-list.js';
diff --git a/src/services/experiments/execution/experiment-execution-service.js b/src/services/experiments/execution/experiment-execution-service.js
index c12d93c2dca2077ea76b04a83fffca22e331b1eb..2ec73f548d385d9f06dd5d5de9476bb08337fd7e 100644
--- a/src/services/experiments/execution/experiment-execution-service.js
+++ b/src/services/experiments/execution/experiment-execution-service.js
@@ -1,7 +1,8 @@
 import _ from 'lodash';
 
 import NrpAnalyticsService from '../../nrp-analytics-service.js';
-import ExperimentServerService from './experiment-server-service.js';
+import ServerResourcesService from './server-resources-service.js';
+import SimulationService from './simulation-service.js';
 import { HttpService } from '../../http-service.js';
 import { EXPERIMENT_STATE } from '../experiment-constants.js';
 
@@ -53,13 +54,13 @@ class ExperimentExecutionService extends HttpService {
     let fatalErrorOccurred = false;
     let serversToTry = experiment.devServer
       ? [experiment.devServer]
-      : ExperimentServerService.instance.getServerAvailability(true).map(s => s.id);
+      : ServerResourcesService.instance.getServerAvailability(true).map(s => s.id);
 
     let brainProcesses = launchSingleMode ? 1 : experiment.configuration.brainProcesses;
 
     //TODO: placeholder, register actual progress callback later
     let progressCallback = (msg) => {
-      //console.info(msg);
+      console.info(msg);
     };
 
     let launchOnNextServer = async () => {
@@ -70,7 +71,7 @@ class ExperimentExecutionService extends HttpService {
       }
 
       let server = nextServer[0];
-      let serverConfig = await ExperimentServerService.instance.getServerConfig(server);
+      let serverConfig = await ServerResourcesService.instance.getServerConfig(server);
 
       return await this.launchExperimentOnServer(
         experiment.id,
@@ -148,14 +149,14 @@ class ExperimentExecutionService extends HttpService {
       progressCallback({ main: 'Initialize Simulation...' });
 
       // register for messages during initialization
-      ExperimentServerService.instance.registerForRosStatusInformation(
+      SimulationService.instance.registerForRosStatusInformation(
         serverConfiguration.rosbridge.websocket,
         progressCallback
       );
 
-      ExperimentServerService.instance.simulationReady(serverURL, simInitData.creationUniqueID)
+      SimulationService.instance.simulationReady(serverURL, simInitData.creationUniqueID)
         .then((simulation) => {
-          ExperimentServerService.instance.initConfigFiles(serverURL, simulation.simulationID)
+          SimulationService.instance.initConfigFiles(serverURL, simulation.simulationID)
             .then(() => {
               let simulationURL = 'esv-private/experiment-view/' + server + '/' + experimentID + '/' +
                 privateExperiment + '/' + simulation.simulationID;
@@ -183,7 +184,7 @@ class ExperimentExecutionService extends HttpService {
         simulationID: simulation.runningSimulation.experimentID
       });*/
 
-      ExperimentServerService.instance
+      ServerResourcesService.instance
         .getServerConfig(simulation.server)
         .then((serverConfig) => {
           let serverURL = serverConfig.gzweb['nrp-services'];
@@ -191,15 +192,15 @@ class ExperimentExecutionService extends HttpService {
 
           function updateSimulationState(state) {
             /*eslint-disable camelcase*/
-            return ExperimentServerService.instance.updateSimulationState(
+            return SimulationService.instance.updateState(
               serverURL,
               simulationID,
               { state: state }
             );
           }
 
-          return ExperimentServerService.instance
-            .getSimulationState(serverURL, simulationID)
+          return SimulationService.instance
+            .getState(serverURL, simulationID)
             .then((data) => {
               if (!data || !data.state) {
                 return Promise.reject();
diff --git a/src/services/experiments/execution/server-resources-service.js b/src/services/experiments/execution/server-resources-service.js
new file mode 100644
index 0000000000000000000000000000000000000000..7c03b2a3f0957358da0f9674e3edd1c8e594735b
--- /dev/null
+++ b/src/services/experiments/execution/server-resources-service.js
@@ -0,0 +1,139 @@
+import _ from 'lodash';
+import { Subject, timer } from 'rxjs';
+import { switchMap, filter, map, multicast } from 'rxjs/operators';
+
+import ErrorHandlerService from '../../error-handler-service.js';
+import { HttpService } from '../../http-service.js';
+
+import endpoints from '../../proxy/data/endpoints.json';
+import config from '../../../config.json';
+const proxyServerURL = `${config.api.proxy.url}${endpoints.proxy.server.url}`;
+const slurmMonitorURL = `${config.api.slurmmonitor.url}/api/v1/partitions/interactive`;
+const availableServersURL = `${config.api.proxy.url}${endpoints.proxy.availableServers.url}`;
+
+let _instance = null;
+const SINGLETON_ENFORCER = Symbol();
+
+const INTERVAL_POLL_SLURM_MONITOR = 5000;
+const INTERVAL_POLL_SERVER_AVAILABILITY = 3000;
+let clusterAvailability = { free: 'N/A', total: 'N/A' };
+
+/**
+ * Service handling server resources for simulating experiments.
+ */
+class ServerResourcesService extends HttpService {
+  constructor(enforcer) {
+    super();
+    if (enforcer !== SINGLETON_ENFORCER) {
+      throw new Error('Use ' + this.constructor.name + '.instance');
+    }
+
+    this.availableServers = [];
+
+    this.startUpdates();
+    window.onbeforeunload = () => {
+      this.stopUpdates();
+    };
+  }
+
+  static get instance() {
+    if (_instance == null) {
+      _instance = new ServerResourcesService(SINGLETON_ENFORCER);
+    }
+
+    return _instance;
+  }
+
+  /**
+   * Start polling updates.
+   */
+  startUpdates() {
+    this.clusterAvailabilityObservable = this._createSlurmMonitorObservable();
+    this.clusterAvailabilitySubscription = this.clusterAvailabilityObservable.subscribe(
+      availability => (clusterAvailability = availability)
+    );
+
+    this.getServerAvailability(true);
+    this.intervalGetServerAvailability = setInterval(
+      () => {
+        this.getServerAvailability(true);
+      },
+      INTERVAL_POLL_SERVER_AVAILABILITY
+    );
+  }
+
+  /**
+   * Stop polling updates.
+   */
+  stopUpdates() {
+    this.clusterAvailabilitySubscription && this.clusterAvailabilitySubscription.unsubscribe();
+    this.intervalGetServerAvailability && clearInterval(this.intervalGetServerAvailability);
+  }
+
+  /**
+   * Get available cluster server info.
+   * @returns {object} cluster availability info
+   */
+  getClusterAvailability() {
+    return clusterAvailability;
+  }
+
+  /**
+   * Return a list of available servers for starting simulations.
+   * @param {boolean} forceUpdate force an update
+   * @returns {Array} A list of available servers.
+   */
+  getServerAvailability(forceUpdate = false) {
+    if (!this.availableServers || forceUpdate) {
+      let update = async () => {
+        let response = await this.httpRequestGET(availableServersURL);
+        this.availableServers = await response.json();
+      };
+      update();
+      this.emit(ServerResourcesService.EVENTS.UPDATE_SERVER_AVAILABILITY, this.availableServers);
+    }
+
+    return this.availableServers;
+  }
+
+  /**
+   * Get the server config for a given server ID.
+   * @param {string} serverID - ID of the server
+   * @returns {object} The server configuration
+   */
+  getServerConfig(serverID) {
+    return this.httpRequestGET(proxyServerURL + '/' + serverID)
+      .then(async (response) => {
+        return await response.json();
+      })
+      .catch(/*serverError.displayHTTPError*/ErrorHandlerService.instance.displayServerHTTPError);
+  }
+
+  _createSlurmMonitorObservable() {
+    return timer(0, INTERVAL_POLL_SLURM_MONITOR)
+      .pipe(switchMap(() => {
+        try {
+          return this.httpRequestGET(slurmMonitorURL);
+        }
+        catch (error) {
+          _.once(error => {
+            if (error.status === -1) {
+              error = Object.assign(error, {
+                data: 'Could not probe vizualization cluster'
+              });
+            }
+            ErrorHandlerService.instance.displayServerHTTPError(error);
+          });
+        }
+      }))
+      .pipe(filter(e => e))
+      .pipe(map(({ free, nodes }) => ({ free, total: nodes[3] })))
+      .pipe(multicast(new Subject())).refCount();
+  }
+}
+
+ServerResourcesService.EVENTS = Object.freeze({
+  UPDATE_SERVER_AVAILABILITY: 'UPDATE_SERVER_AVAILABILITY'
+});
+
+export default ServerResourcesService;
diff --git a/src/services/experiments/execution/experiment-server-service.js b/src/services/experiments/execution/simulation-service.js
similarity index 57%
rename from src/services/experiments/execution/experiment-server-service.js
rename to src/services/experiments/execution/simulation-service.js
index b9f17b2a4f8fb83864d3e797837f50794939681c..6817439f662822827385f52acf8d3b9d41c13445 100644
--- a/src/services/experiments/execution/experiment-server-service.js
+++ b/src/services/experiments/execution/simulation-service.js
@@ -7,132 +7,33 @@ import RoslibService from '../../roslib-service.js';
 import { HttpService } from '../../http-service.js';
 import { EXPERIMENT_STATE } from '../experiment-constants.js';
 
-import endpoints from '../../proxy/data/endpoints.json';
 import config from '../../../config.json';
-const proxyServerURL = `${config.api.proxy.url}${endpoints.proxy.server.url}`;
-const slurmMonitorURL = `${config.api.slurmmonitor.url}/api/v1/partitions/interactive`;
-const availableServersURL = `${config.api.proxy.url}${endpoints.proxy.availableServers.url}`;
 
 let _instance = null;
 const SINGLETON_ENFORCER = Symbol();
 
 let rosConnections = new Map();
-const INTERVAL_POLL__SLURM_MONITOR = 5000;
-const INTERVAL_POLL_SERVER_AVAILABILITY = 3000;
 const INTERVAL_CHECK_SIMULATION_READY = 1000;
-let clusterAvailability = { free: 'N/A', total: 'N/A' };
 
 /**
- * Service handling server resources for simulating experiments.
+ * Service handling state and info of running simulations.
  */
-class ExperimentServerService extends HttpService {
+class SimulationService extends HttpService {
   constructor(enforcer) {
     super();
     if (enforcer !== SINGLETON_ENFORCER) {
       throw new Error('Use ' + this.constructor.name + '.instance');
     }
-
-    //TODO: a bit too much code for a constructor, move into its own function
-    this.clusterAvailabilityObservable = timer(0, INTERVAL_POLL__SLURM_MONITOR)
-      .pipe(switchMap(() => {
-        try {
-          return this.httpRequestGET(slurmMonitorURL);
-        }
-        catch (error) {
-          _.once(error => {
-            if (error.status === -1) {
-              error = Object.assign(error, {
-                data: 'Could not probe vizualization cluster'
-              });
-            }
-            ErrorHandlerService.instance.displayServerHTTPError(error);
-          });
-        }
-      }))
-      .pipe(filter(e => e))
-      .pipe(map(({ free, nodes }) => ({ free, total: nodes[3] })))
-      .pipe(multicast(new Subject())).refCount();
-
-    this.availableServers = [];
-
-    this.startUpdates();
-    window.onbeforeunload = () => {
-      this.stopUpdates();
-    };
   }
 
   static get instance() {
     if (_instance == null) {
-      _instance = new ExperimentServerService(SINGLETON_ENFORCER);
+      _instance = new SimulationService(SINGLETON_ENFORCER);
     }
 
     return _instance;
   }
 
-  /**
-   * Start polling updates.
-   */
-  startUpdates() {
-    this.clusterAvailabilitySubscription = this.clusterAvailabilityObservable.subscribe(
-      availability => (clusterAvailability = availability)
-    );
-
-    this.getServerAvailability(true);
-    this.intervalGetServerAvailability = setInterval(
-      () => {
-        this.getServerAvailability(true);
-      },
-      INTERVAL_POLL_SERVER_AVAILABILITY
-    );
-  }
-
-  /**
-   * Stop polling updates.
-   */
-  stopUpdates() {
-    this.clusterAvailabilitySubscription && this.clusterAvailabilitySubscription.unsubscribe();
-    this.intervalGetServerAvailability && clearInterval(this.intervalGetServerAvailability);
-  }
-
-  /**
-   * Get available cluster server info.
-   * @returns {object} cluster availability info
-   */
-  getClusterAvailability() {
-    return clusterAvailability;
-  }
-
-  /**
-   * Return a list of available servers for starting simulations.
-   * @param {boolean} forceUpdate force an update
-   * @returns {Array} A list of available servers.
-   */
-  getServerAvailability(forceUpdate = false) {
-    if (!this.availableServers || forceUpdate) {
-      let update = async () => {
-        let response = await this.httpRequestGET(availableServersURL);
-        this.availableServers = await response.json();
-      };
-      update();
-      this.emit(ExperimentServerService.EVENTS.UPDATE_SERVER_AVAILABILITY, this.availableServers);
-    }
-
-    return this.availableServers;
-  }
-
-  /**
-   * Get the server config for a given server ID.
-   * @param {string} serverID - ID of the server
-   * @returns {object} The server configuration
-   */
-  getServerConfig(serverID) {
-    return this.httpRequestGET(proxyServerURL + '/' + serverID)
-      .then(async (response) => {
-        return await response.json();
-      })
-      .catch(/*serverError.displayHTTPError*/ErrorHandlerService.instance.displayServerHTTPError);
-  }
-
   /**
    * Initialize config files on a server for a simulation.
    * @param {string} serverBaseUrl - URL of the server
@@ -239,8 +140,13 @@ class ExperimentServerService extends HttpService {
     });
   };
 
-  //TODO: maybe move to separate simulation-status-service
-  async getSimulationState(serverURL, simulationID) {
+  /**
+   * Get the state the simulation is currently in.
+   * @param {string} serverURL URL of the server the simulation is running on
+   * @param {number} simulationID ID of the simulation
+   * @returns {object} The simulation state
+   */
+  async getState(serverURL, simulationID) {
     let url = serverURL + '/simulation/' + simulationID + '/state';
     try {
       let response = await (await this.httpRequestGET(url)).json();
@@ -251,7 +157,13 @@ class ExperimentServerService extends HttpService {
     }
   }
 
-  async updateSimulationState(serverURL, simulationID, state) {
+  /**
+   * Set the state for a simulation.
+   * @param {string} serverURL URL of the server the simulation is running on
+   * @param {number} simulationID ID of the simulation
+   * @param {EXPERIMENT_STATE} state state to set for the simulation
+   */
+  async updateState(serverURL, simulationID, state) {
     let url = serverURL + '/simulation/' + simulationID + '/state';
     try {
       let response = await this.httpRequestPUT(url, JSON.stringify(state));
@@ -263,8 +175,4 @@ class ExperimentServerService extends HttpService {
   }
 }
 
-ExperimentServerService.EVENTS = Object.freeze({
-  UPDATE_SERVER_AVAILABILITY: 'UPDATE_SERVER_AVAILABILITY'
-});
-
-export default ExperimentServerService;
+export default SimulationService;
diff --git a/src/services/experiments/storage/experiment-storage-service.js b/src/services/experiments/storage/experiment-storage-service.js
index a63038cff7c8a97e4a7812ca8531bfb4d731a116..23284d15c77176ade86f5736c963883355785249 100644
--- a/src/services/experiments/storage/experiment-storage-service.js
+++ b/src/services/experiments/storage/experiment-storage-service.js
@@ -12,7 +12,7 @@ const storageImportExperiment = `${config.api.proxy.url}${endpoints.proxy.storag
 let _instance = null;
 const SINGLETON_ENFORCER = Symbol();
 
-const POLL_INTERVAL_EXPERIMENTS = 3000;
+const INTERVAL_POLL_EXPERIMENTS = 3000;
 
 /**
  * Service that fetches the template experiments list from the proxy given
@@ -50,7 +50,7 @@ class ExperimentStorageService extends HttpService {
       () => {
         this.getExperiments(true);
       },
-      POLL_INTERVAL_EXPERIMENTS
+      INTERVAL_POLL_EXPERIMENTS
     );
   }
 
@@ -75,7 +75,6 @@ class ExperimentStorageService extends HttpService {
       this.experiments = await response.json();
       this.sortExperiments();
       await this.fillExperimentDetails();
-      console.info('experiment lsit update');
       this.emit(ExperimentStorageService.EVENTS.UPDATE_EXPERIMENTS, this.experiments);
     }
 
@@ -97,6 +96,9 @@ class ExperimentStorageService extends HttpService {
     return image;
   }
 
+  /**
+   * Sort the local list of experiments alphabetically.
+   */
   sortExperiments() {
     this.experiments = this.experiments.sort(
       (a, b) => {
@@ -113,6 +115,9 @@ class ExperimentStorageService extends HttpService {
     );
   }
 
+  /**
+   * Fill in some details for the local experiment list that might be missing.
+   */
   async fillExperimentDetails() {
     this.experiments.forEach(exp => {
       if (!exp.configuration.brainProcesses && exp.configuration.bibiConfSrc) {