diff --git a/deploy/app.js b/deploy/app.js
index e2260239c9dc507b9079e7a510b2750c20c5d27d..8b815a3607c836f6810190d68ffb632916bc2ba5 100644
--- a/deploy/app.js
+++ b/deploy/app.js
@@ -10,6 +10,19 @@ if (process.env.NODE_ENV !== 'production') {
   app.use(require('cors')())
 }
 
+app.use((req, _, next) => {
+  if (/main\.bundle\.js$/.test(req.originalUrl)){
+    const xForwardedFor = req.headers['x-forwarded-for']
+    const ip = req.connection.remoteAddress
+    console.log({
+      type: 'visitorLog',
+      xForwardedFor,
+      ip
+    })
+  }
+  next()
+})
+
 /**
  * load env first, then load other modules
  */
@@ -44,6 +57,11 @@ const PUBLIC_PATH = process.env.NODE_ENV === 'production'
   ? path.join(__dirname, 'public')
   : path.join(__dirname, '..', 'dist', 'aot')
 
+/**
+ * well known path
+ */
+app.use('/.well-known', express.static(path.join(__dirname, 'well-known')))
+
 app.use(express.static(PUBLIC_PATH))
 
 app.use((req, res, next) => {
diff --git a/deploy/auth/hbp-oidc.js b/deploy/auth/hbp-oidc.js
index cbb7ce83f0d34de5814048a3e2986a660640f40c..a6963750d8469a224c04018c1ef63209cfa9bfc8 100644
--- a/deploy/auth/hbp-oidc.js
+++ b/deploy/auth/hbp-oidc.js
@@ -17,23 +17,27 @@ const cb = (tokenset, {sub, given_name, family_name, ...rest}, done) => {
 }
 
 module.exports = async (app) => {
-  const { oidcStrategy } = await configureAuth({
-    clientId,
-    clientSecret,
-    discoveryUrl,
-    redirectUri,
-    cb,
-    scope: 'openid offline_access',
-    clientConfig: {
-      redirect_uris: [ redirectUri ],
-      response_types: [ 'code' ]
-    }
-  })
-  
-  passport.use('hbp-oidc', oidcStrategy)
-  app.get('/hbp-oidc/auth', passport.authenticate('hbp-oidc'))
-  app.get('/hbp-oidc/cb', passport.authenticate('hbp-oidc', {
-    successRedirect: '/',
-    failureRedirect: '/'
-  }))
+  try {
+    const { oidcStrategy } = await configureAuth({
+      clientId,
+      clientSecret,
+      discoveryUrl,
+      redirectUri,
+      cb,
+      scope: 'openid offline_access',
+      clientConfig: {
+        redirect_uris: [ redirectUri ],
+        response_types: [ 'code' ]
+      }
+    })
+    
+    passport.use('hbp-oidc', oidcStrategy)
+    app.get('/hbp-oidc/auth', passport.authenticate('hbp-oidc'))
+    app.get('/hbp-oidc/cb', passport.authenticate('hbp-oidc', {
+      successRedirect: '/',
+      failureRedirect: '/'
+    }))
+  } catch (e) {
+    console.error(e)
+  }
 }
diff --git a/deploy/auth/index.js b/deploy/auth/index.js
index bfd8fab1724d4a1f052734684b2440baa1374e79..8c3895710418e81e78ef7aa5aea483013a799886 100644
--- a/deploy/auth/index.js
+++ b/deploy/auth/index.js
@@ -14,10 +14,8 @@ module.exports = async (app) => {
 
   passport.deserializeUser((id, done) => {
     const user = objStoreDb.get(id)
-    if (user) 
-      return done(null, user)
-    else
-      return done(null, false)
+    if (user) return done(null, user)
+    else return done(null, false)
   })
 
   await hbpOidc(app)
diff --git a/deploy/catchError.js b/deploy/catchError.js
index 0fdc484348a9e6f8e1f1fc9072eaec5b8de5e1d9..38e1a000f5b78c53d5275bef01caf2ec9cbfb0db 100644
--- a/deploy/catchError.js
+++ b/deploy/catchError.js
@@ -2,7 +2,7 @@ module.exports = ({code = 500, error = 'an error had occured', trace = 'undefine
   /**
    * probably use more elaborate logging?
    */
-  console.log('Catching error', {
+  console.error('Catching error', {
     code,
     error,
     trace
diff --git a/deploy/datasets/index.js b/deploy/datasets/index.js
index edb58cdf6e225786565b1cd7b3a7614f97d952fb..aae9728421904d2487af620abc6a51e18159529d 100644
--- a/deploy/datasets/index.js
+++ b/deploy/datasets/index.js
@@ -106,7 +106,7 @@ const PUBLIC_PATH = process.env.NODE_ENV === 'production'
 const RECEPTOR_PATH = path.join(PUBLIC_PATH, 'res', 'image')
 fs.readdir(RECEPTOR_PATH, (err, files) => {
   if (err) {
-    console.log('reading receptor error', err)
+    console.warn('reading receptor error', err)
     return
   }
   files.forEach(file => previewFileMap.set(`res/image/receptor/${file}`, path.join(RECEPTOR_PATH, file)))
@@ -144,7 +144,7 @@ datasetsRouter.get('/downloadKgFiles', checkKgQuery, cacheMaxAge24Hr, async (req
     res.setHeader('Content-Type', 'application/zip')
     stream.pipe(res)
   } catch (e) {
-    console.log('datasets/index#downloadKgFiles', e)
+    console.warn('datasets/index#downloadKgFiles', e)
     res.status(400).send(e)
   }
 })
diff --git a/deploy/datasets/supplements/previewFile.js b/deploy/datasets/supplements/previewFile.js
index ea178530c1ed118387393dd8754f325b2ff521a2..311ec7d273becf80a84d74615128924cb302551a 100644
--- a/deploy/datasets/supplements/previewFile.js
+++ b/deploy/datasets/supplements/previewFile.js
@@ -13,7 +13,7 @@ let previewMap = new Map(),
 const readFile = (filename) => new Promise((resolve) => {
   fs.readFile(path.join(__dirname, 'data', filename), 'utf-8', (err, data) => {
     if (err){
-      console.log('read file error', err)
+      console.warn('read file error', err)
       return resolve([])
     }
     resolve(JSON.parse(data))
diff --git a/deploy/logging/index.js b/deploy/logging/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..173ec49afb0c3d45332e0c04fbae38283541a3e6
--- /dev/null
+++ b/deploy/logging/index.js
@@ -0,0 +1,43 @@
+const request = require('request')
+const qs = require('querystring')
+
+class Logger {
+  constructor(name, { protocol = 'http', host = 'localhost', port = '24224', username = '', password = '' } = {}){
+    this.name = qs.escape(name)
+    this.protocol = protocol
+    this.host = host
+    this.port = port
+    this.username = username
+    this.password = password
+  }
+
+  emit(logLevel, message, callback){
+    const {
+      name,
+      protocol,
+      host,
+      port,
+      username,
+      password
+    } = this
+    
+    const auth = username !== '' ? `${username}:${password}@` : ''
+    const url = `${protocol}://${auth}${host}:${port}/${name}.${qs.escape(logLevel)}`
+    const formData = {
+      json: JSON.stringify(message)
+    }
+    if (callback) {
+      request.post({
+        url,
+        formData
+      }, callback)
+    } else {
+      return request.post({
+        url,
+        formData
+      })
+    }
+  }
+}
+
+module.exports = Logger
\ No newline at end of file
diff --git a/deploy/server.js b/deploy/server.js
index 85596178cccb7433a77195c447e3efa665cd2583..4256b418128deae6033e4f2bfe29aa94a6c33008 100644
--- a/deploy/server.js
+++ b/deploy/server.js
@@ -5,6 +5,57 @@ if (process.env.NODE_ENV !== 'production') {
   })
 }
 
+if (process.env.FLUENT_HOST) {
+  const Logger = require('./logging')
+
+  const name = process.env.IAV_NAME || 'IAV'
+  const stage = process.env.IAV_STAGE || 'unnamed-stage'
+
+  const protocol = process.env.FLUENT_PROTOCOL || 'http'
+  const host = process.env.FLUENT_HOST || 'localhost'
+  const port = process.env.FLUENT_PORT || 24224
+  
+  const prefix = `${name}.${stage}`
+
+  const log = new Logger(prefix, {
+    protocol,
+    host,
+    port
+  })
+
+  const handleRequestCallback = (err, resp, body) => {
+    if (err) {
+      process.stderr.write(`fluentD logging failed\n`)
+      process.stderr.write(err.toString())
+      process.stderr.write('\n')
+    }
+
+    if (resp && resp.statusCode >= 400) {
+      process.stderr.write(`fluentD logging responded error\n`)
+      process.stderr.write(resp.toString())
+      process.stderr.write('\n')
+    }
+  }
+
+  const emitInfo = message => log.emit('info', { message }, handleRequestCallback)
+
+  const emitWarn = message => log.emit('warn', { message }, handleRequestCallback)
+
+  const emitError = message => log.emit('error', { message }, handleRequestCallback)
+
+  console.log('starting fluentd logging')
+
+  console.log = function () {
+    emitInfo([...arguments])
+  }
+  console.warn = function () {
+    emitWarn([...arguments])
+  }
+  console.emitError = function () {
+    emitError([...arguments])
+  }
+}
+
 const app = require('./app')
 const PORT = process.env.PORT || 3000
 
diff --git a/deploy/well-known/robot.txt b/deploy/well-known/robot.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4f9540ba358a64607438da92eebe85889fdad50a
--- /dev/null
+++ b/deploy/well-known/robot.txt
@@ -0,0 +1 @@
+User-agent: *
\ No newline at end of file
diff --git a/deploy/well-known/security.txt b/deploy/well-known/security.txt
new file mode 100644
index 0000000000000000000000000000000000000000..42a2d3940dfcc87456b84a6165c9e741068c6b31
--- /dev/null
+++ b/deploy/well-known/security.txt
@@ -0,0 +1,2 @@
+# If you would like to report a security issue, please contact us via:
+Contact: inm1-bda@fz-juelich.de
\ No newline at end of file
diff --git a/src/main.module.ts b/src/main.module.ts
index 16755fad9dc20f87b53a44ed3230ae2c94fc348e..7c0b4504e7ac41bf2e358fea9c0f30777e595880 100644
--- a/src/main.module.ts
+++ b/src/main.module.ts
@@ -34,7 +34,7 @@ import { FloatingContainerDirective } from "./util/directives/floatingContainer.
 import { PluginFactoryDirective } from "./util/directives/pluginFactory.directive";
 import { FloatingMouseContextualContainerDirective } from "./util/directives/floatingMouseContextualContainer.directive";
 import { AuthService } from "./services/auth.service";
-import { ViewerConfiguration } from "./services/state/viewerConfig.store";
+import { ViewerConfiguration, LOCAL_STORAGE_CONST } from "./services/state/viewerConfig.store";
 import { FixedMouseContextualContainerDirective } from "./util/directives/FixedMouseContextualContainerDirective.directive";
 import { DatabrowserService } from "./ui/databrowserModule/databrowser.service";
 import { TransformOnhoverSegmentPipe } from "./atlasViewer/onhoverSegment.pipe";
@@ -146,9 +146,13 @@ export class MainModule{
     authServce.authReloadState()
     store.pipe(
       select('viewerConfigState')
-    ).subscribe(({ gpuLimit }) => {
-      if (gpuLimit)
-        window.localStorage.setItem('iv-gpulimit', gpuLimit.toString())
+    ).subscribe(({ gpuLimit, animation }) => {
+      if (gpuLimit) {
+        window.localStorage.setItem(LOCAL_STORAGE_CONST.GPU_LIMIT, gpuLimit.toString())
+      }
+      if (typeof animation !== 'undefined' && animation !== null) {
+        window.localStorage.setItem(LOCAL_STORAGE_CONST.ANIMATION, animation.toString())
+      }
     })
   }
 }
\ No newline at end of file
diff --git a/src/res/css/extra_styles.css b/src/res/css/extra_styles.css
index 899c1be568d579c4f7b4064bb929a930c743624b..19da12b63740924a679c68fea67f8f27b07eefed 100644
--- a/src/res/css/extra_styles.css
+++ b/src/res/css/extra_styles.css
@@ -356,4 +356,17 @@ markdown-dom pre code
 .w-50vw
 {
   width: 50vw!important;
+}
+/* TODO fix hack */
+/* ngx boostrap default z index for modal-container is 1050, which is higher than material tooltip 1000 */
+/* when migration away from ngx bootstrap is complete, remove these classes */
+
+modal-container.modal
+{
+  z-index: 950;
+}
+
+bs-modal-backdrop.modal-backdrop
+{
+  z-index: 940;
 }
\ No newline at end of file
diff --git a/src/services/state/viewerConfig.store.ts b/src/services/state/viewerConfig.store.ts
index eb634fee9dbeda27a7e5915258048bd43b93c885..e6bf3881472833ec2f2adbf939ecbf41f21ee993 100644
--- a/src/services/state/viewerConfig.store.ts
+++ b/src/services/state/viewerConfig.store.ts
@@ -21,16 +21,29 @@ export const CONFIG_CONSTANTS = {
 }
 
 export const ACTION_TYPES = {
+  SET_ANIMATION: `SET_ANIMATION`,
   UPDATE_CONFIG: `UPDATE_CONFIG`,
   CHANGE_GPU_LIMIT: `CHANGE_GPU_LIMIT`
 }
 
-const lsGpuLimit = localStorage.getItem('iv-gpulimit')
+export const LOCAL_STORAGE_CONST = {
+  GPU_LIMIT: 'iv-gpulimit',
+  ANIMATION: 'iv-animationFlag'
+}
+
+const lsGpuLimit = localStorage.getItem(LOCAL_STORAGE_CONST.GPU_LIMIT)
+const lsAnimationFlag = localStorage.getItem(LOCAL_STORAGE_CONST.ANIMATION)
 const gpuLimit = lsGpuLimit && !isNaN(Number(lsGpuLimit))
   ? Number(lsGpuLimit)
   : CONFIG_CONSTANTS.defaultGpuLimit
 
-export function viewerConfigState(prevState:ViewerConfiguration = {animation: CONFIG_CONSTANTS.defaultAnimation, gpuLimit}, action:ViewerConfigurationAction) {
+const animation = lsAnimationFlag && lsAnimationFlag === 'true'
+  ? true
+  : lsAnimationFlag === 'false'
+    ? false
+    : CONFIG_CONSTANTS.defaultAnimation
+
+export function viewerConfigState(prevState:ViewerConfiguration = {animation, gpuLimit}, action:ViewerConfigurationAction) {
   switch (action.type) {
     case ACTION_TYPES.UPDATE_CONFIG:
       return {
diff --git a/src/ui/config/config.component.ts b/src/ui/config/config.component.ts
index e0104405a1b4bc8efda4e8d5b8e18af4598b7b06..12a3a700a6f84bdf71fa77699c0be0b772756385 100644
--- a/src/ui/config/config.component.ts
+++ b/src/ui/config/config.component.ts
@@ -2,7 +2,11 @@ import { Component, OnInit, OnDestroy } from '@angular/core'
 import { Store, select } from '@ngrx/store';
 import { ViewerConfiguration, ACTION_TYPES } from 'src/services/state/viewerConfig.store'
 import { Observable, Subject, Subscription } from 'rxjs';
-import { map, distinctUntilChanged, debounce, debounceTime } from 'rxjs/operators';
+import { map, distinctUntilChanged, debounceTime } from 'rxjs/operators';
+import { MatSlideToggleChange } from '@angular/material';
+
+const GPU_TOOLTIP = `GPU TOOLTIP`
+const ANIMATION_TOOLTIP = `ANIMATION_TOOLTIP`
 
 @Component({
   selector: 'config-component',
@@ -14,10 +18,15 @@ import { map, distinctUntilChanged, debounce, debounceTime } from 'rxjs/operator
 
 export class ConfigComponent implements OnInit, OnDestroy{
 
+  public GPU_TOOLTIP = GPU_TOOLTIP
+  public ANIMATION_TOOLTIP = ANIMATION_TOOLTIP
+
   /**
    * in MB
    */
   public gpuLimit$: Observable<number>
+
+  public animationFlag$: Observable<boolean>
   public keydown$: Subject<Event> = new Subject()
   private subscriptions: Subscription[] = []
 
@@ -31,6 +40,11 @@ export class ConfigComponent implements OnInit, OnDestroy{
       distinctUntilChanged(),
       map(v => v / 1e6)
     )
+
+    this.animationFlag$ = this.store.pipe(
+      select('viewerConfigState'),
+      map((config:ViewerConfiguration) => config.animation),
+    )
   }
 
   ngOnInit(){
@@ -64,6 +78,16 @@ export class ConfigComponent implements OnInit, OnDestroy{
     })
   }
 
+  public toggleAnimationFlag(ev: MatSlideToggleChange ){
+    const { checked } = ev
+    this.store.dispatch({
+      type: ACTION_TYPES.UPDATE_CONFIG,
+      config: {
+        animation: checked
+      }
+    })
+  }
+
   public setGpuPreset({value}: {value: number}) {
     this.store.dispatch({
       type: ACTION_TYPES.UPDATE_CONFIG,
diff --git a/src/ui/config/config.template.html b/src/ui/config/config.template.html
index 18f84d0b297f9ef71422960695f032c567faafb1..dca3b164d0fbe6bd4235e50e856073835acb13d0 100644
--- a/src/ui/config/config.template.html
+++ b/src/ui/config/config.template.html
@@ -1,7 +1,8 @@
-<div class="input-group">
+<div class="input-group mb-2">
   <span class="input-group-prepend">
     <span class="input-group-text">
       GPU Limit
+      <small tooltipClass="z-1060" [matTooltip]="GPU_TOOLTIP" class="ml-2 fas fa-question"></small>
     </span>
   </span>
   <input
@@ -17,16 +18,25 @@
   <div class="input-group-append">
 
     <div (click)="setGpuPreset({ value: 100 })" class="btn btn-outline-secondary">
-        100
-      </div>
-      <div (click)="setGpuPreset({ value: 500 })" class="btn btn-outline-secondary">
-        500
-      </div>
-      <div (click)="setGpuPreset({ value: 1000 })" class="btn btn-outline-secondary">
-        1000
-      </div>
-      <span class="input-group-text">
-        MB
-      </span>
+      100
     </div>
-  </div>
\ No newline at end of file
+    <div (click)="setGpuPreset({ value: 500 })" class="btn btn-outline-secondary">
+      500
+    </div>
+    <div (click)="setGpuPreset({ value: 1000 })" class="btn btn-outline-secondary">
+      1000
+    </div>
+    <span class="input-group-text">
+      MB
+    </span>
+  </div>
+</div>
+
+<div class="d-flex align-items-center">
+  <mat-slide-toggle
+    [checked]="animationFlag$ | async"
+    (change)="toggleAnimationFlag($event)">
+    Enable Animation
+  </mat-slide-toggle>
+  <small [matTooltip]="ANIMATION_TOOLTIP" class="ml-2 fas fa-question"></small>
+</div>
\ No newline at end of file
diff --git a/src/ui/sharedModules/angularMaterial.module.ts b/src/ui/sharedModules/angularMaterial.module.ts
index b63e2dfaa5a37e5caa567efebe4bf1c135e00a4b..fe3b30ad0b03a82f351a0d90fab08e89d821f06d 100644
--- a/src/ui/sharedModules/angularMaterial.module.ts
+++ b/src/ui/sharedModules/angularMaterial.module.ts
@@ -4,12 +4,13 @@ import {
   MatSidenavModule,
   MatCardModule,
   MatTabsModule,
-  MatTooltipModule
+  MatTooltipModule,
+  MatSlideToggleModule
 } from '@angular/material';
 import { NgModule } from '@angular/core';
 
 @NgModule({
-  imports: [MatButtonModule, MatCheckboxModule, MatSidenavModule, MatCardModule, MatTabsModule, MatTooltipModule],
-  exports: [MatButtonModule, MatCheckboxModule, MatSidenavModule, MatCardModule, MatTabsModule, MatTooltipModule],
+  imports: [MatSlideToggleModule, MatButtonModule, MatCheckboxModule, MatSidenavModule, MatCardModule, MatTabsModule, MatTooltipModule],
+  exports: [MatSlideToggleModule, MatButtonModule, MatCheckboxModule, MatSidenavModule, MatCardModule, MatTabsModule, MatTooltipModule],
 })
 export class AngularMaterialModule { }
\ No newline at end of file