diff --git a/.github/workflows/python-app_linux.yml b/.github/workflows/python-app_linux.yml
new file mode 100644
index 0000000..42371c7
--- /dev/null
+++ b/.github/workflows/python-app_linux.yml
@@ -0,0 +1,51 @@
+# This workflow will install Python dependencies, run tests and lint with a single version of Python
+# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
+
+name: BlipBlop app linux
+
+on:
+  push:
+    branches: [ master ]
+  pull_request:
+    branches: [ master ]
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python 3.8
+      uses: actions/setup-python@v2
+      with:
+        python-version: 3.8
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install pyqt5 pyinstaller flake8
+        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
+    
+    - name: Lint with flake8
+      run: |
+        # stop the build if there are Python syntax errors or undefined names
+        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
+        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
+        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
+    
+    - name: create package
+      run: |
+        pyrcc5 resources.qrc -o resources.py
+        pyinstaller blipblop_linux.spec
+    
+    - name: Upload a Build Artifact
+      uses: actions/upload-artifact@v2.2.2
+      with:
+        # Artifact name
+        name: BlibBlop-linux
+        # A file, directory or wildcard pattern that describes what to upload
+        path: |
+          dist
+        retention-days: 1
+    
+    
diff --git a/.github/workflows/python-app_macos.yml b/.github/workflows/python-app_macos.yml
index 3401d98..a4f2761 100644
--- a/.github/workflows/python-app_macos.yml
+++ b/.github/workflows/python-app_macos.yml
@@ -1,7 +1,7 @@
 # This workflow will install Python dependencies, run tests and lint with a single version of Python
 # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
 
-name: Python application
+name: BlipBlop app macOS
 
 on:
   push:
@@ -36,13 +36,13 @@ jobs:
     - name: create package
       run: |
         pyrcc5 resources.qrc -o resources.py
-        pyinstaller --onefile --windowed --name="BlipBlop" --osx-bundle-identifier="de.uni-tuebingen.neuroetho.blipblop" -i icons/blipblop_logo.icns --add-data="icons/blipblop_logo.icns:." --add-data="resources.py:." blipblop.py
+        pyinstaller --osx-bundle-identifier="de.uni-tuebingen.neuroetho.blipblop" blipblop_darwin.spec
     
     - name: Upload a Build Artifact
       uses: actions/upload-artifact@v2.2.2
       with:
         # Artifact name
-        name: blipblop-binary
+        name: BlipBlop-macOS
         # A file, directory or wildcard pattern that describes what to upload
         path: |
           dist
diff --git a/.github/workflows/python-app_win.yml b/.github/workflows/python-app_win.yml
index 8f64119..14ec40f 100644
--- a/.github/workflows/python-app_win.yml
+++ b/.github/workflows/python-app_win.yml
@@ -1,7 +1,7 @@
 # This workflow will install Python dependencies, run tests and lint with a single version of Python
 # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
 
-name: Python application
+name: BlipBlop app windows
 
 on:
   push:
@@ -24,7 +24,6 @@ jobs:
       run: |
         python -m pip install --upgrade pip
         pip install pyqt5 pyinstaller flake8
-        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
     
     - name: Lint with flake8
       run: |
@@ -36,7 +35,7 @@ jobs:
     - name: create package
       run: |
         pyrcc5 resources.qrc -o resources.py
-        pyinstaller --onefile --windowed --name="BlipBlop" -i icons/blipblop_logo.ico --add-data="icons/blipblop_logo.icns;." --add-data="resources.py;." blipblop.py
+        pyinstaller blipblop_win.spec
     
     - name: Upload a Build Artifact
       uses: actions/upload-artifact@v2.2.2
diff --git a/.gitignore b/.gitignore
index 4ef3e36..df8847a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,5 @@
 *.pyc
-*__pycache__
\ No newline at end of file
+*__pycache__
+resources.py
+build
+dist
\ No newline at end of file
diff --git a/blipblop/constants.py b/blipblop/constants.py
index 10b6dfd..46f9a80 100644
--- a/blipblop/constants.py
+++ b/blipblop/constants.py
@@ -4,36 +4,47 @@ from PyQt5.QtGui import QIcon
 from PyQt5.QtMultimedia import QMediaContent
 from PyQt5.QtCore import QUrl
 
-import resources
+import resources  # needs to be imported somewhere in the project to be picked up by qt
 
-organization = "neuroetho.uni-tuebingen.de"
-application = "blipblop"
-version = 0.1
+organization_name = "de.uni-tuebingen.neuroetho"
+application_name = "BlipBlop"
+application_version = 0.1
 
 PACKAGE_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
 ICONS_FOLDER = os.path.join(PACKAGE_ROOT, "icons")
 DOCS_ROOT_FILE = os.path.join(PACKAGE_ROOT, "docs", "index.md")
 SNDS_FOLDER = os.path.join(PACKAGE_ROOT, "sounds")
 
+# Find and list sounds
+SNDS_PATHS = glob.glob(os.path.join(SNDS_FOLDER, "*.wav"))
+SNDS_PATHS = sorted(SNDS_PATHS)
+SNDS_DICT = {}
+
+for snd in SNDS_PATHS:
+    SNDS_DICT[snd.split(os.sep)[-1].split(".")[0]] = snd
+
+""" This snippet is kept because it shows how to iterate the qt resources.py file
+it = QDirIterator(":", QDirIterator.Subdirectories);
+while it.hasNext():
+    name = it.next()
+    if "sounds/" in name:
+        SNDS_DICT[name.split("/")[-1]] = "qrc" + name
+"""
+
+# Find and list icons and images
 ICONS_PATHS = glob.glob(os.path.join(ICONS_FOLDER, "*.png"))
 ICONS_PATHS.extend(glob.glob(os.path.join(ICONS_FOLDER, "*.icns")))
 ICONS_PATHS = sorted(ICONS_PATHS)
 ICON_DICT = {}
 
-SNDS_PATHS = glob.glob(os.path.join(SNDS_FOLDER, "*.wav"))
-SNDS_PATHS = sorted(SNDS_PATHS)
-SNDS_DICT = {}
-
 for icon in ICONS_PATHS:
     ICON_DICT[icon.split(os.sep)[-1].split(".")[0]] = icon
 
-for snd in SNDS_PATHS:
-    SNDS_DICT[snd.split(os.sep)[-1].split(".")[0]] = snd
-
 
 def get_sound(name):
     if name in SNDS_DICT.keys():
-        return QMediaContent(QUrl.fromLocalFile(os.path.abspath(SNDS_DICT[name])))
+        url = QUrl.fromLocalFile(SNDS_DICT[name])
+        return QMediaContent(url)
     else:
         print("Sound %s not found!" % name)
         return None
@@ -43,5 +54,4 @@ def get_icon(name):
     if name in ICON_DICT.keys():
         return QIcon(ICON_DICT[name])
     else:
-        return QIcon("nix_logo.png")
-
+        return QIcon("blipblop_logo.png")
diff --git a/blipblop/main.py b/blipblop/main.py
index c4fe103..6929a56 100644
--- a/blipblop/main.py
+++ b/blipblop/main.py
@@ -1,5 +1,5 @@
-#!/usr/bin/python3
 import sys
+from PyQt5.QtGui import QIcon
 from PyQt5.QtWidgets import QApplication
 from PyQt5.QtCore import QSettings
 from blipblop.ui.mainwindow import BlipBlop
@@ -8,22 +8,22 @@ import blipblop.constants as cnst
 try:
     # Include in try/except block if you're also targeting Mac/Linux
     from PyQt5.QtWinExtras import QtWin
-    myappid = 'neuroetho.uni-tuebingen.de.blipblop.0.1'
+    myappid = "%s.%s" %(cnst.organization_name, cnst.application_version)
     QtWin.setCurrentProcessExplicitAppUserModelID(myappid)
 except ImportError:
     pass
 
 def main():
     app = QApplication(sys.argv)
-    app.setApplicationName("blipblop")
-    app.setApplicationVersion("0.1")
-    app.setOrganizationDomain("neuroetho.uni-tuebingen.de")
-    app.setWindowIcon(cnst.get_icon("blipblop_logo.png"))
+    app.setApplicationName(cnst.application_name)
+    app.setApplicationVersion(str(cnst.application_version))
+    app.setOrganizationDomain(cnst.organization_name)
+    app.setWindowIcon(QIcon(":/icons/app_icon_png"))
     settings = QSettings()
-    width = settings.value("app/width", 1024)
-    height = settings.value("app/height", 768)
-    x = settings.value("app/pos_x", 100)
-    y = settings.value("app/pos_y", 100)
+    width = int(settings.value("app/width", 1024))
+    height = int(settings.value("app/height", 768))
+    x = int(settings.value("app/pos_x", 100))
+    y = int(settings.value("app/pos_y", 100))
     window = BlipBlop()
     window.setMinimumWidth(800)
     window.setMinimumHeight(600)
@@ -38,7 +38,3 @@ def main():
     settings.setValue("app/pos_x", pos.x())
     settings.setValue("app/pos_y", pos.y())
     sys.exit(code)
-    
-
-if __name__ == "__main__":
-    main()
\ No newline at end of file
diff --git a/blipblop/ui/audioblop.py b/blipblop/ui/audioblop.py
index 7a5536a..518ad2d 100644
--- a/blipblop/ui/audioblop.py
+++ b/blipblop/ui/audioblop.py
@@ -1,105 +1,13 @@
-from PyQt5.QtWidgets import QAction, QComboBox, QFormLayout, QGridLayout, QLabel, QPushButton, QSizePolicy, QSlider, QSpinBox, QSplitter, QTextEdit, QVBoxLayout, QWidget
+from PyQt5.QtWidgets import QAction, QGridLayout, QLabel, QPushButton, QSizePolicy, QSplitter, QVBoxLayout, QWidget
 from PyQt5.QtCore import QPoint, QRandomGenerator, QTimer, Qt, pyqtSignal
 from PyQt5.QtGui import QColor, QFont, QIcon, QKeySequence, QPainter, QPen, QPixmap
 from PyQt5.QtMultimedia import QMediaPlayer
 
-import os
 import blipblop.constants as cnst
 from blipblop.ui.countdownlabel import CountdownLabel
+from blipblop.ui.settings import AuditoryTaskSettings
 import datetime as dt
 
-class SettingsPanel(QWidget):
-    def __init__(self, parent=None):
-        super().__init__(parent=parent)
-
-        self._trial_spinner = QSpinBox()
-        self._trial_spinner.setMinimum(5)
-        self._trial_spinner.setMaximum(25)
-        self._trial_spinner.setValue(5)
-        self._trial_spinner.setToolTip("Number of consecutive trials (5 - 25)")
-
-        self._min_delay_spinner = QSpinBox()
-        self._min_delay_spinner.setMinimum(1)
-        self._min_delay_spinner.setMaximum(10)
-        self._min_delay_spinner.setValue(1)
-        self._min_delay_spinner.setToolTip("Minimum delay between start of trial and stimulus display [s]")
-        
-        self._max_delay_spinner = QSpinBox() 
-        self._max_delay_spinner.setMinimum(1)
-        self._max_delay_spinner.setMaximum(10)
-        self._max_delay_spinner.setValue(5)
-        self._max_delay_spinner.setToolTip("Maximum delay between start of trial and stimulus display [s]")
-        
-        self._countdown_spinner = QSpinBox() 
-        self._countdown_spinner.setMinimum(1)
-        self._countdown_spinner.setMaximum(10)
-        self._countdown_spinner.setValue(3)
-        self._countdown_spinner.setToolTip("Pause between trials [s]")
-        
-        self._saliency_slider = QSlider(Qt.Horizontal)
-        self._saliency_slider.setMinimum(0)
-        self._saliency_slider.setMaximum(100)
-        self._saliency_slider.setSliderPosition(100)
-        self._saliency_slider.setTickInterval(25)
-        self._saliency_slider.setTickPosition(QSlider.TicksBelow)
-        self._saliency_slider.setToolTip("Saliency of the stimulus, i.e. its loudness")
-
-        self._sound_combo = QComboBox()
-        for k in cnst.SNDS_DICT.keys():
-            self._sound_combo.addItem(k)
-
-        self._instructions = QTextEdit()
-        self._instructions.setMarkdown("* fixate central cross\n * press start (enter) when ready\n * press space bar as soon as the stimulus occurs")
-        self._instructions.setMinimumHeight(200)
-        self._instructions.setReadOnly(True)
-
-        form_layout = QFormLayout()
-        form_layout.addRow("Settings", None)
-        form_layout.addRow("number of trials", self._trial_spinner)
-        form_layout.addRow("minimum delay [s]", self._min_delay_spinner)
-        form_layout.addRow("maximum delay [s]", self._max_delay_spinner)
-        form_layout.addRow("pause [s]", self._countdown_spinner)
-        form_layout.addRow("stimulus saliency", self._saliency_slider)
-        form_layout.addRow("stimulus sound", self._sound_combo)
-        form_layout.addRow("instructions", self._instructions)
-        self.setLayout(form_layout)
-
-    @property
-    def trials(self):
-        return self._trial_spinner.value()
-    
-    @property
-    def saliency(self):
-        return self._saliency_slider.sliderPosition()
-    
-    @property
-    def size(self):
-        return self._size_slider.sliderPosition()
-    
-    @property
-    def min_delay(self):
-        return self._min_delay_spinner.value()
-    
-    @property
-    def max_delay(self):
-        return self._max_delay_spinner.value()
-    
-    @property
-    def countdown(self):
-        return self._countdown_spinner.value()
-    
-    @property
-    def sound(self):
-        return self._sound_combo.currentText()
-    
-    def set_enabled(self, enabled):
-        self._trial_spinner.setEnabled(enabled)
-        self._saliency_slider.setEnabled(enabled)
-        self._countdown_spinner.setEnabled(enabled)
-        self._min_delay_spinner.setEnabled(enabled)
-        self._max_delay_spinner.setEnabled(enabled)
-        self._sound_combo.setEnabled(False)
-
 
 class AudioBlop(QWidget):
     task_done = pyqtSignal()
@@ -107,7 +15,7 @@ class AudioBlop(QWidget):
 
     def __init__(self, parent=None) -> None:
         super().__init__(parent=parent)
-       
+
         widget = QWidget()
         grid = QGridLayout()
         grid.setColumnStretch(0, 1)
@@ -116,37 +24,37 @@ class AudioBlop(QWidget):
         grid.setRowStretch(3, 1)
         widget.setLayout(grid)
 
-        l = QLabel("Auditory reaction test")
-        l.setPixmap(QPixmap(":/icons/auditory_task"))
-        grid.addWidget(l, 0, 0, Qt.AlignLeft)
-        
-        l2 =QLabel("Measurement of auditory reaction times\npress enter to start")
+        icon_label = QLabel("Auditory reaction test")
+        icon_label.setPixmap(QPixmap(":/icons/auditory_task"))
+        grid.addWidget(icon_label, 0, 0, Qt.AlignLeft)
+
+        heading_label = QLabel("Measurement of auditory reaction times\npress enter to start")
         font = QFont()
         font.setBold(True)
         font.setPointSize(20)
-        l2.setFont(font)
-        l2.setStyleSheet("color: #2D4B9A")
-        grid.addWidget(l2, 1, 0, 1, 2, Qt.AlignLeft)
-        
+        heading_label.setFont(font)
+        heading_label.setStyleSheet("color: #2D4B9A")
+        grid.addWidget(heading_label, 1, 0, 1, 2, Qt.AlignLeft)
+
         settings_btn = QPushButton(QIcon(":/icons/settings"), "")
         settings_btn.setToolTip("edit task settings")
         settings_btn.setShortcut(QKeySequence("alt+s"))
         settings_btn.clicked.connect(self.on_toggle_settings)
         grid.addWidget(settings_btn, 0, 3, Qt.AlignRight)
-        
+
         self._status_label = QLabel("Ready to start, press enter ...")
-        grid.addWidget(self._status_label, 3, 0, Qt.AlignLeft)
+        grid.addWidget(self._status_label, 4, 0, Qt.AlignLeft)
 
         self._countdown_label = CountdownLabel(text="Next trial in:")
-        grid.addWidget(self._countdown_label, 3, 1, Qt.AlignCenter)
+        grid.addWidget(self._countdown_label, 4, 1, Qt.AlignCenter)
         self._countdown_label.countdown_done.connect(self.run_trial)
-        
+
         self._draw_area = QLabel()
         self._draw_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
         grid.addWidget(self._draw_area, 2, 1)
-        
-        self._settings = SettingsPanel()
-        
+
+        self._settings = AuditoryTaskSettings()
+
         self._splitter = QSplitter()
         self._splitter.addWidget(widget)
         self._splitter.addWidget(self._settings)
@@ -155,7 +63,7 @@ class AudioBlop(QWidget):
         vbox = QVBoxLayout()
         vbox.addWidget(self._splitter)
         self.setLayout(vbox)
-        
+
         self.reset_canvas()
         self.create_actions()
 
@@ -168,12 +76,12 @@ class AudioBlop(QWidget):
         self._player = QMediaPlayer()
         self._random_generator = QRandomGenerator()
         self.setFocus()
-    
+
     def create_actions(self):
         self._start_action = QAction("start trial")
         self._start_action.setShortcuts([QKeySequence("enter"), QKeySequence("return")])
         self._start_action.triggered.connect(self.on_trial_start)
-        
+
         self._reaction = QAction("reaction")
         self._reaction.setShortcut(QKeySequence("space"))
         self._reaction.triggered.connect(self.on_reaction)
@@ -181,7 +89,7 @@ class AudioBlop(QWidget):
         self._abort = QAction("abort")
         self._abort.setShortcut(QKeySequence("escape"))
         self._abort.triggered.connect(self.on_abort)
-        
+
         self.addAction(self._start_action)
         self.addAction(self._reaction)
         self.addAction(self._abort)
@@ -189,7 +97,7 @@ class AudioBlop(QWidget):
     def on_reaction(self):
         if not self._session_running or not self._trial_running:
             return
-        
+
         self._response_time = dt.datetime.now()
         if self._trial_counter < self._settings.trials:
             self._status_label.setText("Trial %i of %i" % (self._trial_counter, self._settings.trials))
@@ -206,14 +114,13 @@ class AudioBlop(QWidget):
             return
         self._countdown_label.start(self._settings.countdown)
 
-
     def reset_canvas(self):
         bkg_color = QColor()
         bkg_color.setAlphaF(0.0)
         canvas = QPixmap(400, 400)
         self._canvas_center = QPoint(200, 200)
         canvas.fill(bkg_color)
-        self.draw_fixation(canvas)        
+        self.draw_fixation(canvas)
         self._draw_area.setPixmap(canvas)
         self._draw_area.update()
 
@@ -222,7 +129,7 @@ class AudioBlop(QWidget):
         right = QPoint(225, 200)
         top = QPoint(200, 175)
         bottom = QPoint(200, 225)
-        painter = QPainter(pixmap)    
+        painter = QPainter(pixmap)
         painter.setPen(QPen(Qt.red, 2, Qt.SolidLine))
         painter.drawLine(left, right)
         painter.drawLine(top, bottom)
@@ -234,12 +141,13 @@ class AudioBlop(QWidget):
     def blip(self):
         self._player.play()
         self._start_time = dt.datetime.now()
-    
+
     def on_trial_start(self):
         if self._trial_running:
             return
         if not self._session_running:
             self._settings.set_enabled(False)
+            self._settings.store_settings()
             self._session_running = True
         self._countdown_label.start(time=self._settings.countdown)
 
@@ -251,9 +159,10 @@ class AudioBlop(QWidget):
         self._trial_counter += 1
         self._status_label.setText("Trial %i of %i running" % (self._trial_counter, self._settings.trials))
         content = cnst.get_sound(self._settings.sound)
+
         self._player.setMedia(content)
         self._player.setVolume(self._settings.saliency)
-       
+
         min_interval = int(self._settings.min_delay * 10)
         max_interval = int(self._settings.max_delay * 10)
         interval = self._random_generator.bounded(min_interval, max_interval) * 100
@@ -263,15 +172,15 @@ class AudioBlop(QWidget):
         self._timer.setInterval(int(interval))
         self._timer.timeout.connect(self.blip)
         self._timer.start()
-    
+
     def on_abort(self):
         self.reset()
         self.task_aborted.emit()
-    
+
     @property
     def results(self):
         return self._reaction_times
-        
+
     def reset(self):
         self._trial_counter = 0
         self._session_running = 0
@@ -281,7 +190,7 @@ class AudioBlop(QWidget):
         self._status_label.setText("Ready to start...")
         self._settings.set_enabled(True)
         self._countdown_label.stop()
-        
+
     def on_toggle_settings(self):
         if self._splitter.sizes()[1] > 0:
             self._splitter.widget(1).hide()
diff --git a/blipblop/ui/centralwidget.py b/blipblop/ui/centralwidget.py
index 0780b5f..360e60a 100644
--- a/blipblop/ui/centralwidget.py
+++ b/blipblop/ui/centralwidget.py
@@ -52,6 +52,7 @@ class CentralWidget(QWidget):
 
     def reset(self):
         self._task_results = []
+        self._results_screen.reset()
         self._stack.setCurrentIndex(0)
         
     def on_new_visual_task(self):
diff --git a/blipblop/ui/filescreen.py b/blipblop/ui/filescreen.py
deleted file mode 100644
index 8ea6c98..0000000
--- a/blipblop/ui/filescreen.py
+++ /dev/null
@@ -1,142 +0,0 @@
-from PyQt5.QtWidgets import QComboBox, QFrame, QGroupBox, QHBoxLayout, QLabel, QSplitter, QTextEdit, QVBoxLayout, QWidget
-from PyQt5.QtCore import QItemSelectionModel, Qt
-
-from nixview.util.file_handler import FileHandler
-from nixview.util.descriptors import ItemDescriptor
-import nixview.communicator as comm
-import nixview.constants as cnst
-from nixview.data_models.tree_model import NixTreeView, TreeModel, TreeType
-
-
-class FileScreen(QWidget):
-    def __init__(self, parent=None) -> None:
-        super().__init__(parent=parent)
-        self._file_handler = FileHandler()
-        
-        vbox = QVBoxLayout()
-        self.setLayout(vbox)
-        
-        main_splitter = QSplitter(Qt.Vertical)
-        self.layout().addWidget(main_splitter)
-        
-        self._info = EntityInfo(self)
-        main_splitter.addWidget(self._info)
-       
-        self._data_tree = NixTreeView(self)
-        
-        self._tree_type_combo = QComboBox()
-        self._tree_type_combo.adjustSize()
-        self._tree_type_combo.addItems([TreeType.Data.value, TreeType.Full.value, TreeType.Metadata.value])
-        self._tree_type_combo.currentTextChanged.connect(self.update)
-        
-        hbox = QHBoxLayout()
-        hbox.addWidget(QLabel("Tree type:"))
-        hbox.addWidget(self._tree_type_combo)
-        hbox.addStretch()
-        data_group = QGroupBox("Data")
-        data_vbox = QVBoxLayout()
-        data_vbox.setContentsMargins(1, 10, 1, 1)
-        
-        data_vbox.addLayout(hbox)
-        data_vbox.addWidget(self._data_tree)
-        data_group.setLayout(data_vbox)
-        
-        main_splitter.addWidget(data_group)
-        main_splitter.setSizes([200, 600])
-        vbox.addWidget(main_splitter)
-
-    def dataTreeSelection(self, current_index, last_index):
-        if not current_index.isValid():
-            return
-        item = current_index.internalPointer()
-        comm.communicator.item_selected.emit(item)
-        self._info.setEntityInfo(item.node_descriptor)
-
-    def update(self):
-        tt = TreeType.Data
-        if self._tree_type_combo.currentText() == TreeType.Data.value:
-            tt = TreeType.Data
-        elif self._tree_type_combo.currentText() == TreeType.Full.value:
-            tt = TreeType.Full
-        elif self._tree_type_combo.currentText() == TreeType.Metadata.value:
-            tt = TreeType.Metadata
-        self._info.setEntityInfo(None)
-        data_model = TreeModel(self._file_handler, tt)
-        self._data_tree.setModel(data_model)
-        selection_model = QItemSelectionModel(data_model)
-        self._data_tree.setSelectionModel(selection_model)
-        selection_model.currentChanged.connect(self.dataTreeSelection)
-        for i in range(data_model.columnCount(None)):
-            self._data_tree.resizeColumnToContents(i)
-        self._info.setFileInfo(self._file_handler.file_descriptor)
-        
-    def reset(self):
-        pass
-
-
-class EntityInfo(QWidget):
-
-    def __init__(self, parent):
-        super().__init__(parent=parent)
-        self._file_handler = FileHandler()
-        self.setLayout(QHBoxLayout())
-        
-        self._metadata_tree = NixTreeView()
-    
-        mdata_grp = QGroupBox("Metadata")
-        mdata_grp.setLayout(QVBoxLayout())
-        mdata_grp.layout().setContentsMargins(1, 10, 1, 1)
-        mdata_grp.layout().addWidget(self._metadata_tree)
-        
-        file_info_grp = QGroupBox("File info")
-        file_info_grp.setLayout(QVBoxLayout())
-        file_info_grp.layout().setContentsMargins(1, 10, 1, 1)
-        self._file_info = QTextEdit("File information")
-        self._file_info.setEnabled(True)
-        self._file_info.setTextInteractionFlags(Qt.TextSelectableByKeyboard | Qt.TextSelectableByMouse)
-        self._file_info.setFrameShape(QFrame.NoFrame)
-        self._file_info.setLineWrapMode(QTextEdit.WidgetWidth)
-        file_info_grp.layout().addWidget(self._file_info)
-
-        entity_info_grp = QGroupBox("Entity info")
-        entity_info_grp.setLayout(QVBoxLayout())
-        entity_info_grp.layout().setContentsMargins(1, 10, 1, 1)
-        self._entity_info = QTextEdit("Entity information")
-        self._file_info.setEnabled(True)
-        self._file_info.setTextInteractionFlags(Qt.TextSelectableByKeyboard | Qt.TextSelectableByMouse)
-        self._file_info.setFrameShape(QFrame.NoFrame)
-        self._file_info.setLineWrapMode(QTextEdit.WidgetWidth)
-        entity_info_grp.layout().addWidget(self._entity_info)
- 
-        self._splitter = QSplitter(Qt.Horizontal)
-        self._splitter.addWidget(file_info_grp)
-        self._splitter.addWidget(entity_info_grp)
-        self._splitter.addWidget(mdata_grp)
-        self._splitter.setSizes([200, 400, 0])
-        self._splitter.setStretchFactor(0, 0)
-        self._splitter.setStretchFactor(1, 1)
-        self._splitter.setStretchFactor(2, 1)
-        
-        self.layout().addWidget(self._splitter)
-        
-
-    def setFileInfo(self, file_info):
-        if file_info is not None:
-            self._file_info.setText(file_info.toHtml())
-            
-    def setEntityInfo(self, entity_info):
-        if entity_info is None or not isinstance(entity_info, ItemDescriptor):
-            self._splitter.setSizes([200, 400, 0])
-            self._entity_info.setText("")
-            self._metadata_tree.setModel(None)
-            return
-
-        if entity_info.metadata_id is not None:
-            self._splitter.setSizes([200, 400, 400])
-        else:
-            self._splitter.setSizes([200, 400, 0])
-        self._entity_info.setText(entity_info.to_html())
-        metadata_model = TreeModel(self._file_handler, TreeType.Metadata, root_section_id=entity_info.metadata_id)
-        self._metadata_tree.setModel(metadata_model)
-        for i in range(metadata_model.columnCount(None)):
-            self._metadata_tree.resizeColumnToContents(i)
diff --git a/blipblop/ui/help.py b/blipblop/ui/help.py
index 00f68d0..15f3854 100644
--- a/blipblop/ui/help.py
+++ b/blipblop/ui/help.py
@@ -1,30 +1,40 @@
 from PyQt5.QtGui import QIcon
-from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QFrame, QHBoxLayout, QPushButton, QSizePolicy, QTextBrowser, QVBoxLayout, QWidget
-from PyQt5.QtCore import QUrl
+from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QFrame, QHBoxLayout
+from PyQt5.QtWidgets import QPushButton, QSizePolicy, QTextBrowser, QVBoxLayout, QWidget
+from PyQt5.QtCore import QSettings, QUrl, Qt
 
 import blipblop.constants as cnst
 
+
 class HelpDialog(QDialog):
-    
-    def __init__(self, parent = None) -> None:
+
+    def __init__(self, parent=None) -> None:
         super().__init__(parent=parent)
-        
-        self.setModal(True)
-        self.setMinimumSize(500, 750)
+        self._settings = QSettings()
+        width = int(self._settings.value("help/width", 640))
+        height = int(self._settings.value("help/height", 480))
+        x = int(self._settings.value("help/pos_x", 100))
+        y = int(self._settings.value("help/pos_y", 100))
 
+        self.setModal(True)
+        self.setMinimumSize(640, 480)
+        self.resize(width, height)
+        self.move(x, y)
+        self.finished.connect(self.on_finished)
         self.help = HelpBrowser()
 
         self.help._edit.historyChanged.connect(self._on_history_changed)
-        
-        self.back_btn = QPushButton(QIcon(":/icons/back_btn"), "back")
+
+        self.back_btn = QPushButton(QIcon(":/icons/docs_back"), "back")
         self.back_btn.setEnabled(False)
         self.back_btn.clicked.connect(self.help._edit.backward)
-        self.home_btn = QPushButton(QIcon(":/icons/home_btn"),"home")
+        self.home_btn = QPushButton(QIcon(":/icons/docs_home"), "home")
         self.home_btn.clicked.connect(self.help._edit.home)
-        self.fwd_btn = QPushButton(QIcon(":/icons/fwd_btn"),"forward")
+        self.fwd_btn = QPushButton(QIcon(":/icons/docs_forward"), "forward")
+        self.fwd_btn.setLayoutDirection(Qt.RightToLeft)
         self.fwd_btn.setEnabled(False)
         self.fwd_btn.clicked.connect(self.help._edit.forward)
-        
+
         empty = QWidget()
         empty.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
 
@@ -32,8 +42,8 @@ class HelpDialog(QDialog):
         hbox.addWidget(self.back_btn)
         hbox.addWidget(self.home_btn)
         hbox.addWidget(self.fwd_btn)
-        hbox.addWidget(empty)      
-        
+        hbox.addWidget(empty)
+
         bbox = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok)
         bbox.accepted.connect(self.accept)
         layout = QVBoxLayout()
@@ -42,17 +52,23 @@ class HelpDialog(QDialog):
         layout.addWidget(self.help)
         layout.addWidget(bbox)
         self.setLayout(layout)
-        
+
     def _on_history_changed(self):
         self.back_btn.setEnabled(self.help._edit.isBackwardAvailable())
         self.fwd_btn.setEnabled(self.help._edit.isForwardAvailable())
 
+    def on_finished(self):
+        self._settings.setValue("help/width", self.width())
+        self._settings.setValue("help/height", self.height())
+        self._settings.setValue("help/pos_x", self.x())
+        self._settings.setValue("help/pos_y", self.y())
+
 
 class HelpBrowser(QWidget):
+
     def __init__(self, parent=None) -> None:
         super().__init__(parent=parent)
         self.setLayout(QVBoxLayout())
-        # FIXME https://stackoverflow.com/a/43217828  about loading from esource files
         doc_url = QUrl.fromLocalFile(cnst.DOCS_ROOT_FILE)
         self._edit = QTextBrowser()
         self._edit.setOpenLinks(True)
diff --git a/blipblop/ui/mainwindow.py b/blipblop/ui/mainwindow.py
index 38d995b..726b66f 100644
--- a/blipblop/ui/mainwindow.py
+++ b/blipblop/ui/mainwindow.py
@@ -24,17 +24,17 @@ class BlipBlop(QMainWindow):
         self.show()
     
     def create_actions(self):
-        self._quit_action = QAction(QIcon(":/icons/quit"), "Quit", self)
+        self._quit_action = QAction(QIcon(":/icons/quit"), "quit application", self)
         self._quit_action.setStatusTip("Quit BlipBlop")
         self._quit_action.setShortcut(QKeySequence("Ctrl+q"))
         self._quit_action.triggered.connect(self.on_quit)
 
-        self._new_action = QAction(QIcon(":/icons/new_session"), "New session", self)
+        self._new_action = QAction(QIcon(":/icons/new_session"), "new session", self)
         self._new_action.setStatusTip("Start a new session discarding previous results")
         self._new_action.setShortcut(QKeySequence("Ctrl+n"))
         self._new_action.triggered.connect(self.on_new)
 
-        self._results_action = QAction(QIcon(":/icons/results_table"), "Show results", self)
+        self._results_action = QAction(QIcon(":/icons/results_table"), "show results", self)
         self._results_action.setStatusTip("Show results as table")
         self._results_action.setShortcut(QKeySequence("Ctrl+r"))
         self._results_action.setEnabled(True)
@@ -51,13 +51,13 @@ class BlipBlop(QMainWindow):
         self._help_action.setEnabled(True)
         self._help_action.triggered.connect(self.on_help)
 
-        self._visual_task_action = QAction(QIcon(":/icons/visual_task"), "visual")
+        self._visual_task_action = QAction(QIcon(":/icons/visual_task"), "visual task")
         self._visual_task_action.setStatusTip("Start measuring visual reaction times")
         self._visual_task_action.setShortcut(QKeySequence("Ctrl+1"))
         self._visual_task_action.setEnabled(True)
         self._visual_task_action.triggered.connect(self.on_visual)
 
-        self._auditory_task_action = QAction(QIcon(":/icons/auditory_task"), "auditory")
+        self._auditory_task_action = QAction(QIcon(":/icons/auditory_task"), "auditory task")
         self._auditory_task_action.setStatusTip("Start measuring auditory reaction times")
         self._auditory_task_action.setShortcut(QKeySequence("Ctrl+2"))
         self._auditory_task_action.setEnabled(True) 
@@ -77,6 +77,7 @@ class BlipBlop(QMainWindow):
         self._toolbar.addAction(self._visual_task_action)
         self._toolbar.addAction(self._auditory_task_action)
         self._toolbar.addAction(self._results_action)
+        self._toolbar.addSeparator()
         self._toolbar.addAction(self._help_action)
         
         empty = QWidget()
diff --git a/blipblop/ui/resultsscreen.py b/blipblop/ui/resultsscreen.py
index ac062a0..a439552 100644
--- a/blipblop/ui/resultsscreen.py
+++ b/blipblop/ui/resultsscreen.py
@@ -1,20 +1,19 @@
 import io
 import csv
 from PyQt5 import QtWidgets
-from PyQt5.QtGui import QFont, QKeySequence
+from PyQt5.QtGui import QFont, QIcon, QKeySequence
 from PyQt5.QtWidgets import QAction, QLabel, QStackedLayout, QTableWidget, QTableWidgetItem, QWidget
 from PyQt5.QtCore import Qt, pyqtSignal
 
-import blipblop.constants as cnst
 from blipblop.util.results import MeasurementResults
 
 
 class ResultsScreen(QWidget):
     back_signal = pyqtSignal()
-    
+
     def __init__(self, parent=None) -> None:
         super().__init__(parent=parent)
-        
+
         self.table = QTableWidget()
         self._stack = QStackedLayout(self)
         label = QLabel("There are no results to show\n(press ESC to go back)")
@@ -27,34 +26,34 @@ class ResultsScreen(QWidget):
         self._stack.addWidget(label)         # 0
         self._stack.addWidget(self.table)    # 1
         self.setLayout(self._stack)
-        
+
         self._back_action = QAction("back")
         self._back_action.setShortcut(QKeySequence("escape"))
         self._back_action.triggered.connect(self.on_back)
-        
+
         self._copy_action = QAction("copy")
         self._copy_action.setShortcut(QKeySequence("ctrl+c"))
         self._copy_action.triggered.connect(self.copy_selection)
 
         self.addAction(self._back_action)
         self.addAction(self._copy_action)
-        
+
     def set_results(self, measurement_results):
         if len(measurement_results) == 0:
             return
-    
+
         for mr in measurement_results:
             if not isinstance(mr, MeasurementResults):
                 print("Some result entries are no MeasurementResults!")
-                return     
-        
+                return
+
         row_count = max([len(r.results) for r in measurement_results])
         col_count = len(measurement_results)
         self.table.setRowCount(row_count)
         self.table.setColumnCount(col_count)
 
         for col, mr in enumerate(measurement_results):
-            headerItem = QTableWidgetItem(cnst.get_icon("visual_task") if "visual" in mr.name.lower() else cnst.get_icon("auditory_task"), "")
+            headerItem = QTableWidgetItem(QIcon(":/icons/visual_task") if "visual" in mr.name.lower() else QIcon(":/icons/auditory_task"), "")
             headerItem.setToolTip("%s started at\n %s " % (mr.name, mr.starttime))
             self.table.setHorizontalHeaderItem(col, headerItem)
             for row, r in enumerate(mr.results):
@@ -89,4 +88,3 @@ class ResultsScreen(QWidget):
     def reset(self):
         self.table.clear()
         self._stack.setCurrentIndex(0)
-
diff --git a/blipblop/ui/settings.py b/blipblop/ui/settings.py
new file mode 100644
index 0000000..7535694
--- /dev/null
+++ b/blipblop/ui/settings.py
@@ -0,0 +1,148 @@
+from PyQt5.QtWidgets import QComboBox, QFormLayout, QSlider, QSpinBox, QTextEdit, QWidget
+from PyQt5.QtCore import Qt, QSettings
+import blipblop.constants as cnst
+
+
+class TaskSettings(QWidget):
+
+    def __init__(self, parent=None):
+        super().__init__(parent=parent)
+        self.settings = QSettings()
+        
+        self._trial_spinner = QSpinBox()
+        self._trial_spinner.setMinimum(5)
+        self._trial_spinner.setMaximum(25)
+        self._trial_spinner.setValue(int(self.settings.value("task/trials", 5)))
+        self._trial_spinner.setToolTip("Number of consecutive trials (5 - 25)")
+
+        self._min_delay_spinner = QSpinBox()
+        self._min_delay_spinner.setMinimum(1)
+        self._min_delay_spinner.setMaximum(10)
+        self._min_delay_spinner.setValue(int(self.settings.value("task/min_delay", 1)))
+        self._min_delay_spinner.setToolTip("Minimum delay between start of trial and stimulus display [s]")
+
+        self._max_delay_spinner = QSpinBox()
+        self._max_delay_spinner.setMinimum(1)
+        self._max_delay_spinner.setMaximum(10)
+        self._max_delay_spinner.setValue(int(self.settings.value("task/max_delay", 5)))
+        self._max_delay_spinner.setToolTip("Maximum delay between start of trial and stimulus display [s]")
+
+        self._countdown_spinner = QSpinBox()
+        self._countdown_spinner.setMinimum(1)
+        self._countdown_spinner.setMaximum(10)
+        self._countdown_spinner.setValue(int(self.settings.value("task/pause", 3)))
+        self._countdown_spinner.setToolTip("Pause between trials [s]")
+
+        self._saliency_slider = QSlider(Qt.Horizontal)
+        self._saliency_slider.setMinimum(0)
+        self._saliency_slider.setMaximum(100)
+        self._saliency_slider.setSliderPosition(int(self.settings.value("task/saliency", 100)))
+        self._saliency_slider.setTickInterval(25)
+        self._saliency_slider.setTickPosition(QSlider.TicksBelow)
+
+        self._instructions = QTextEdit()
+        self._instructions.setMarkdown("* fixate central cross\n * press start (enter) when ready\n * press space bar as soon as the stimulus occurs")
+        self._instructions.setMinimumHeight(200)
+        self._instructions.setReadOnly(True)
+
+        self.form_layout = QFormLayout()
+        self.form_layout.addRow("Settings", None)
+        self.form_layout.addRow("number of trials", self._trial_spinner)
+        self.form_layout.addRow("minimum delay [s]", self._min_delay_spinner)
+        self.form_layout.addRow("maximum delay [s]", self._max_delay_spinner)
+        self.form_layout.addRow("pause [s]", self._countdown_spinner)
+        self.form_layout.addRow("stimulus saliency", self._saliency_slider)
+        self.form_layout.addRow("instructions", self._instructions)
+        self.setLayout(self.form_layout)
+
+    @property
+    def trials(self):
+        return self._trial_spinner.value()
+
+    @property
+    def saliency(self):
+        return self._saliency_slider.sliderPosition()
+
+    @property
+    def size(self):
+        return self._size_slider.sliderPosition()
+
+    @property
+    def min_delay(self):
+        return self._min_delay_spinner.value()
+
+    @property
+    def max_delay(self):
+        return self._max_delay_spinner.value()
+
+    @property
+    def countdown(self):
+        return self._countdown_spinner.value()
+
+    def set_enabled(self, enabled):
+        self._trial_spinner.setEnabled(enabled)
+        self._saliency_slider.setEnabled(enabled)
+        self._countdown_spinner.setEnabled(enabled)
+        self._min_delay_spinner.setEnabled(enabled)
+        self._max_delay_spinner.setEnabled(enabled)
+
+    def store_settings(self):
+        self.settings.setValue("task/trials", self.trials)
+        self.settings.setValue("task/salience", self.saliency)
+        self.settings.setValue("task/min_delay", self.min_delay)
+        self.settings.setValue("task/max_delay", self.max_delay)
+        self.settings.setValue("task/pause", self.countdown)
+
+
+class AuditoryTaskSettings(TaskSettings):
+
+    def __init__(self, parent=None):
+        super().__init__(parent=parent)
+        self._saliency_slider.setToolTip("Saliency of the stimulus, i.e. its loudness")
+
+        self._sound_combo = QComboBox()
+        for k in cnst.SNDS_DICT.keys():
+            self._sound_combo.addItem(k)
+        self._sound_combo.setCurrentIndex(int(self.settings.value("auditory_task/sound_index", 0)))
+        self.form_layout.insertRow(self.form_layout.rowCount() - 1, "stimulus sound", self._sound_combo)
+
+    def set_enabled(self, enabled):
+        super().set_enabled(enabled)
+        self._sound_combo.setEnabled(enabled)
+
+    def store_settings(self):
+        super().store_settings()
+        self.settings.setValue("auditory_task/sound_index", self._sound_combo.currentIndex())
+
+    @property
+    def sound(self):
+        return self._sound_combo.currentText()
+
+
+class VisualTaskSettings(TaskSettings):
+
+    def __init__(self, parent=None):
+        super().__init__(parent=parent)
+        self._saliency_slider.setToolTip("Saliency of the stimulus, i.e. its opacity")
+
+        self._size_slider = QSlider(Qt.Horizontal)
+        self._size_slider.setMinimum(0)
+        self._size_slider.setMaximum(200)
+        self._size_slider.setSliderPosition(int(self.settings.value("visual_task/stimulus_size", 100)))
+        self._size_slider.setTickInterval(25)
+        self._size_slider.setTickPosition(QSlider.TicksBelow)
+        self._size_slider.setToolTip("Diameter of the stimulus in pixel")
+
+        self.form_layout.insertRow(self.form_layout.rowCount() - 1, "stimulus size", self._size_slider)
+
+    def set_enabled(self, enabled):
+        super().set_enabled(enabled)
+        self._size_slider.setEnabled(enabled)
+
+    def store_settings(self):
+        super().store_settings()
+        self.settings.setValue("visual_task/stimulus_size", self.size)
+
+    @property
+    def size(self):
+        return self._size_slider.sliderPosition()
diff --git a/blipblop/ui/startscreen.py b/blipblop/ui/startscreen.py
index 107ea02..f5eb498 100644
--- a/blipblop/ui/startscreen.py
+++ b/blipblop/ui/startscreen.py
@@ -1,13 +1,11 @@
-import os
 from PyQt5.QtWidgets import QWidget, QGridLayout, QLabel
 from PyQt5.QtGui import QFont, QPixmap
 from PyQt5.QtCore import Qt, pyqtSignal
 
-import blipblop.constants as cnst
-
 
 class MyLabel(QLabel):
     clicked = pyqtSignal()
+
     def mouseReleaseEvent(self, QMouseEvent):
         if QMouseEvent.button() == Qt.LeftButton:
             self.clicked.emit()
@@ -23,11 +21,11 @@ class StartScreen(QWidget):
         layout = QGridLayout()
         layout.setColumnStretch(0, 1)
         layout.setColumnStretch(6, 1)
-       
+
         layout.setRowStretch(0, 1)
         layout.setRowStretch(4, 1)
         self.setLayout(layout)
-        
+
         label = QLabel("Measure your reaction times!\nselect a task")
         font = QFont()
         font.setBold(True)
@@ -36,24 +34,26 @@ class StartScreen(QWidget):
         label.setFont(font)
         label.setAlignment(Qt.AlignCenter)
         layout.addWidget(label, 1, 2, 1, 3, Qt.AlignCenter)
-        
+
         visual_task_label = MyLabel()
+        visual_task_label.setStatusTip("Start a new visual measurement (Ctrl+1)")
         visual_task_label.setToolTip("Click to start a new visual task (Ctrl+1)")
         visual_task_label.setPixmap(QPixmap(":/icons/visual_task_large"))
         visual_task_label.setMaximumWidth(256)
         visual_task_label.clicked.connect(self.new_visual_task)
-        
+
         auditory_task_label = MyLabel()
-        auditory_task_label.setToolTip("Click to start a new auditory task (Ctrl+2)")
+        auditory_task_label.setStatusTip("Start a new auditory measurement (Ctrl+2)")
+        auditory_task_label.setToolTip("click to start a new auditory task (Ctrl+2)")
         auditory_task_label.setPixmap(QPixmap(":/icons/auditory_task_large"))
         auditory_task_label.setMaximumWidth(256)
         auditory_task_label.clicked.connect(self.new_auditory_task)
-        
-        layout.addWidget(visual_task_label, 2, 1, 2, 2, Qt.AlignCenter )
-        layout.addWidget(auditory_task_label, 2, 4, 2, 2, Qt.AlignCenter )
+
+        layout.addWidget(visual_task_label, 2, 1, 2, 2, Qt.AlignCenter)
+        layout.addWidget(auditory_task_label, 2, 4, 2, 2, Qt.AlignCenter)
 
     def new_auditory_task(self):
         self.auditory_task_signal.emit()
-    
+
     def new_visual_task(self):
         self.visual_task_signal.emit()
diff --git a/blipblop/ui/visualblip.py b/blipblop/ui/visualblip.py
index 0608dc7..eac2c72 100644
--- a/blipblop/ui/visualblip.py
+++ b/blipblop/ui/visualblip.py
@@ -1,104 +1,11 @@
-from PyQt5.QtWidgets import QAction, QFormLayout, QGridLayout, QLabel, QLineEdit, QPushButton, QSizePolicy, QSlider, QSpinBox, QSplitter, QTextEdit, QVBoxLayout, QWidget
-from PyQt5.QtCore import QPoint, QRandomGenerator, QTimer, Qt, pyqtSignal, QSettings
-from PyQt5.QtGui import QColor, QFont, QKeySequence, QPainter, QBrush, QPen, QPixmap
+from PyQt5.QtWidgets import QAction, QGridLayout, QLabel, QPushButton, QSizePolicy, QSplitter, QVBoxLayout, QWidget
+from PyQt5.QtCore import QPoint, QRandomGenerator, QTimer, Qt, pyqtSignal
+from PyQt5.QtGui import QColor, QFont, QIcon, QKeySequence, QPainter, QBrush, QPen, QPixmap
 
-import os
-import blipblop.constants as cnst
 from blipblop.ui.countdownlabel import CountdownLabel
+from blipblop.ui.settings import VisualTaskSettings
 import datetime as dt
 
-class SettingsPanel(QWidget):
-    def __init__(self, parent=None):
-        super().__init__(parent=parent)
-
-        self._trial_spinner = QSpinBox()
-        self._trial_spinner.setMinimum(5)
-        self._trial_spinner.setMaximum(25)
-        self._trial_spinner.setValue(5)
-        self._trial_spinner.setToolTip("Number of consecutive trials (5 - 25)")
-
-        self._min_delay_spinner = QSpinBox()
-        self._min_delay_spinner.setMinimum(1)
-        self._min_delay_spinner.setMaximum(10)
-        self._min_delay_spinner.setValue(1)
-        self._min_delay_spinner.setToolTip("Minimum delay between start of trial and stimulus display [s]")
-        
-        self._max_delay_spinner = QSpinBox() 
-        self._max_delay_spinner.setMinimum(1)
-        self._max_delay_spinner.setMaximum(10)
-        self._max_delay_spinner.setValue(5)
-        self._max_delay_spinner.setToolTip("Maximum delay between start of trial and stimulus display [s]")
-        
-        self._saliency_slider = QSlider(Qt.Horizontal)
-        self._saliency_slider.setMinimum(0)
-        self._saliency_slider.setMaximum(100)
-        self._saliency_slider.setSliderPosition(100)
-        self._saliency_slider.setTickInterval(25)
-        self._saliency_slider.setTickPosition(QSlider.TicksBelow)
-        self._saliency_slider.setToolTip("Saliency of the stimulus, i.e. its opacity")
-
-        self._size_slider = QSlider(Qt.Horizontal)
-        self._size_slider.setMinimum(0)
-        self._size_slider.setMaximum(200)
-        self._size_slider.setSliderPosition(100)
-        self._size_slider.setTickInterval(25)
-        self._size_slider.setTickPosition(QSlider.TicksBelow)
-        self._size_slider.setToolTip("Diameter of the stimulus in pixel")
-        
-        self._countdown_spinner = QSpinBox()
-        self._countdown_spinner.setMinimum(2)
-        self._countdown_spinner.setMaximum(30)
-        self._countdown_spinner.setValue(5)
-        self._countdown_spinner.setToolTip("Pause/countdown for next trial")
-        
-        self._instructions = QTextEdit()
-        self._instructions.setMarkdown("* fixate central cross\n * press start (enter) when ready\n * press space bar as soon as the stimulus occurs")
-        self._instructions.setMinimumHeight(200)
-        self._instructions.setReadOnly(True)
-
-        form_layout = QFormLayout()
-        form_layout.addRow("Settings", None)
-        form_layout.addRow("number of trials", self._trial_spinner)
-        form_layout.addRow("pause until next trial [s]", self._countdown_spinner)
-        form_layout.addRow("minimum delay [s]", self._min_delay_spinner)
-        form_layout.addRow("maximum delay [s]", self._max_delay_spinner)
-        form_layout.addRow("stimulus saliency", self._saliency_slider)
-        form_layout.addRow("stimulus size", self._size_slider)
-        form_layout.addRow("instructions", self._instructions)
-        self.setLayout(form_layout)
-
-    @property
-    def trials(self):
-        return self._trial_spinner.value()
-    
-    @property
-    def saliency(self):
-        return self._saliency_slider.sliderPosition()
-    
-    @property
-    def size(self):
-        return self._size_slider.sliderPosition()
-    
-    @property
-    def min_delay(self):
-        return self._min_delay_spinner.value()
-    
-    @property
-    def max_delay(self):
-        return self._max_delay_spinner.value()
-    
-    @property
-    def countdown(self):
-        return self._countdown_spinner.value()
-    
-    def set_enabled(self, enabled):
-        self._trial_spinner.setEnabled(enabled)
-        self._saliency_slider.setEnabled(enabled)
-        self._size_slider.setEnabled(enabled)
-        self._countdown_spinner.setEnabled(enabled)
-        self._min_delay_spinner.setEnabled(enabled)
-        self._max_delay_spinner.setEnabled(enabled)
-
 
 class VisualBlip(QWidget):
     task_done = pyqtSignal()
@@ -106,7 +13,7 @@ class VisualBlip(QWidget):
 
     def __init__(self, parent=None) -> None:
         super().__init__(parent=parent)
-        
+
         widget = QWidget()
         grid = QGridLayout()
         grid.setColumnStretch(0, 1)
@@ -115,37 +22,37 @@ class VisualBlip(QWidget):
         grid.setRowStretch(3, 1)
         widget.setLayout(grid)
 
-        l = QLabel("Visual reaction test")
-        l.setPixmap(QPixmap(os.path.join(cnst.ICONS_FOLDER, "visual_task.png")))
-        grid.addWidget(l, 0, 0, Qt.AlignLeft)
+        icon_label = QLabel("Visual reaction test")
+        icon_label.setPixmap(QPixmap(":/icons/visual_task"))
+        grid.addWidget(icon_label, 0, 0, Qt.AlignLeft)
 
-        l2 = QLabel("Measurement of visual reaction times\npress enter to start")
+        heading_label = QLabel("Measurement of visual reaction times\npress enter to start")
         font = QFont()
         font.setBold(True)
         font.setPointSize(20)
-        l2.setFont(font)
-        l2.setStyleSheet("color: #2D4B9A")
-        grid.addWidget(l2, 1, 0, 1, 2, Qt.AlignLeft)
-        
-        settings_btn = QPushButton(cnst.get_icon("settings"), "")
+        heading_label.setFont(font)
+        heading_label.setStyleSheet("color: #2D4B9A")
+        grid.addWidget(heading_label, 1, 0, 1, 2, Qt.AlignLeft)
+
+        settings_btn = QPushButton(QIcon(":/icons/settings"), "")
         settings_btn.setToolTip("edit task settings")
         settings_btn.setShortcut(QKeySequence("alt+s"))
         settings_btn.clicked.connect(self.on_toggle_settings)
         grid.addWidget(settings_btn, 0, 3, Qt.AlignRight)
-        
+
         self._status_label = QLabel("Ready to start, press enter ...")
-        grid.addWidget(self._status_label, 3, 0, Qt.AlignBaseline)
-        
+        grid.addWidget(self._status_label, 4, 0, Qt.AlignBaseline)
+
         self._countdown_label = CountdownLabel(text="Next trial in:")
-        grid.addWidget(self._countdown_label, 3, 1, Qt.AlignCenter)
+        grid.addWidget(self._countdown_label, 4, 1, Qt.AlignCenter)
         self._countdown_label.countdown_done.connect(self.run_trial)
-        
+
         self._draw_area = QLabel()
         self._draw_area.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
         grid.addWidget(self._draw_area, 2, 1)
-        
-        self._settings = SettingsPanel()
-        
+
+        self._settings = VisualTaskSettings()
+
         self._splitter = QSplitter()
         self._splitter.addWidget(widget)
         self._splitter.addWidget(self._settings)
@@ -154,7 +61,7 @@ class VisualBlip(QWidget):
         vbox = QVBoxLayout()
         vbox.addWidget(self._splitter)
         self.setLayout(vbox)
-        
+
         self.reset_canvas()
         self.create_actions()
 
@@ -167,12 +74,12 @@ class VisualBlip(QWidget):
         self._random_generator = QRandomGenerator()
 
         self.setFocus()
-    
+
     def create_actions(self):
         self._start_action = QAction("start trial")
         self._start_action.setShortcuts([QKeySequence("enter"), QKeySequence("return")])
         self._start_action.triggered.connect(self.on_trial_start)
-        
+
         self._reaction = QAction("reaction")
         self._reaction.setShortcut(QKeySequence("space"))
         self._reaction.triggered.connect(self.on_reaction)
@@ -180,7 +87,7 @@ class VisualBlip(QWidget):
         self._abort = QAction("abort")
         self._abort.setShortcut(QKeySequence("escape"))
         self._abort.triggered.connect(self.on_abort)
-        
+
         self.addAction(self._start_action)
         self.addAction(self._reaction)
         self.addAction(self._abort)
@@ -188,7 +95,7 @@ class VisualBlip(QWidget):
     def on_reaction(self):
         if not self._session_running or not self._trial_running:
             return
-        
+
         self._response_time = dt.datetime.now()
         if self._trial_counter < self._settings.trials:
             self._status_label.setText("Trial %i of %i" % (self._trial_counter, self._settings.trials))
@@ -212,7 +119,7 @@ class VisualBlip(QWidget):
         canvas = QPixmap(400, 400)
         self._canvas_center = QPoint(200, 200)
         canvas.fill(bkg_color)
-        self.draw_fixation(canvas)        
+        self.draw_fixation(canvas)
         self._draw_area.setPixmap(canvas)
         self._draw_area.update()
 
@@ -221,7 +128,7 @@ class VisualBlip(QWidget):
         right = QPoint(225, 200)
         top = QPoint(200, 175)
         bottom = QPoint(200, 225)
-        painter = QPainter(pixmap)    
+        painter = QPainter(pixmap)
         painter.setPen(QPen(Qt.red, 2, Qt.SolidLine))
         painter.drawLine(left, right)
         painter.drawLine(top, bottom)
@@ -232,24 +139,25 @@ class VisualBlip(QWidget):
 
     def blip(self):
         stim_size = self._settings.size
-        painter = QPainter(self._draw_area.pixmap())    
+        painter = QPainter(self._draw_area.pixmap())
         painter.setPen(QPen(Qt.red,  1, Qt.SolidLine))
         color = QColor(Qt.red)
         color.setAlphaF(self._settings.saliency/100)
-        painter.setBrush(QBrush(color, Qt.SolidPattern))      
+        painter.setBrush(QBrush(color, Qt.SolidPattern))
         painter.drawEllipse(self._canvas_center, stim_size, stim_size)
         painter.end()
         self._start_time = dt.datetime.now()
         self._draw_area.update()
-    
+
     def on_trial_start(self):
         if self._trial_running:
             return
         if not self._session_running:
             self._settings.set_enabled(False)
-            self._session_running = True 
+            self._settings.store_settings()
+            self._session_running = True
         self._countdown_label.start(time=self._settings.countdown)
-        
+
     def run_trial(self):
         self._trial_running = True
         if self._trial_counter >= self._settings.trials:
@@ -267,15 +175,15 @@ class VisualBlip(QWidget):
         self._timer.setInterval(int(interval))
         self._timer.timeout.connect(self.blip)
         self._timer.start()
-    
+
     def on_abort(self):
         self.reset()
         self.task_aborted.emit()
-    
+
     @property
     def results(self):
         return self._reaction_times
-        
+
     def reset(self):
         self.reset_canvas()
         self._trial_counter = 0
@@ -286,7 +194,7 @@ class VisualBlip(QWidget):
         self._status_label.setText("Ready to start...")
         self._countdown_label.stop()
         self._settings.set_enabled(True)
-        
+
     def on_toggle_settings(self):
         if self._splitter.sizes()[1] > 0:
             self._splitter.widget(1).hide()
diff --git a/blipblop_darwin.spec b/blipblop_darwin.spec
new file mode 100644
index 0000000..76e5105
--- /dev/null
+++ b/blipblop_darwin.spec
@@ -0,0 +1,48 @@
+# -*- mode: python ; coding: utf-8 -*-
+
+block_cipher = None
+
+
+a = Analysis(['blipblop_main.py'],
+             pathex=['.'],
+             binaries=[],
+             datas=[('docs/index.md', "docs"), 
+                    ('docs/visual_task.md', "docs"), 
+                    ('docs/auditory_task.md', "docs"),
+                    ('docs/license.md', "docs"),
+                    ('docs/tasks.md', "docs"),
+                    ('docs/images/blipblop_main.png', "docs/images"),
+                    ('docs/images/blipblop_logo.png', "docs/images"),
+                    ('sounds/bell.wav', "sounds"),
+                    ('sounds/complete.wav', "sounds"),
+                    ('sounds/message.wav', "sounds"),
+                    ('icons/blipblop_logo.icns', "."),
+                    ],
+             hiddenimports=[],
+             hookspath=[],
+             runtime_hooks=[],
+             excludes=[],
+             win_no_prefer_redirects=False,
+             win_private_assemblies=False,
+             cipher=block_cipher,
+             noarchive=False)
+
+pyz = PYZ(a.pure, a.zipped_data,
+             cipher=block_cipher)
+
+exe = EXE(pyz,
+          a.scripts,
+          a.binaries,
+          a.zipfiles,
+          a.datas,
+          [],
+          name='BlipBlop',
+          debug=True,
+          bootloader_ignore_signals=False,
+          strip=False,
+          upx=True,
+          upx_exclude=[],
+          runtime_tmpdir=None,
+          console=True,
+          icon='icons/blipblop_logo.icns'
+          )
diff --git a/blipblop_linux.spec b/blipblop_linux.spec
new file mode 100644
index 0000000..7bad729
--- /dev/null
+++ b/blipblop_linux.spec
@@ -0,0 +1,51 @@
+# -*- mode: python ; coding: utf-8 -*-
+
+block_cipher = None
+
+
+a = Analysis(['blipblop_main.py'],
+             pathex=['.'],
+             binaries=[],
+             datas=[('docs/index.md', "docs"), 
+                    ('docs/visual_task.md', "docs"), 
+                    ('docs/auditory_task.md', "docs"),
+                    ('docs/license.md', "docs"),
+                    ('docs/results.md', "docs"),
+                    ('docs/tasks.md', "docs"),
+                    ('docs/images/blipblop_main.png', "docs/images"),
+                    ('docs/images/blipblop_logo.png', "docs/images"),
+                    ('sounds/bell.wav', "sounds"),
+                    ('sounds/complete.wav', "sounds"),
+                    ('sounds/message.wav', "sounds"),
+                    ],
+             hiddenimports=[],
+             hookspath=[],
+             runtime_hooks=[],
+             excludes=[],
+             win_no_prefer_redirects=False,
+             win_private_assemblies=False,
+             cipher=block_cipher,
+             noarchive=False)
+
+pyz = PYZ(a.pure, a.zipped_data,
+             cipher=block_cipher)
+
+exe = EXE(pyz,
+          a.scripts,
+          [],
+          exclude_binaries=True,
+          name='BlipBlop',
+          debug=False,
+          bootloader_ignore_signals=False,
+          strip=False,
+          upx=True,
+          console=False )
+
+coll = COLLECT(exe,
+               a.binaries,
+               a.zipfiles,
+               a.datas,
+               strip=False,
+               upx=True,
+               upx_exclude=[],
+               name='BlipBlop')
diff --git a/blipblop.py b/blipblop_main.py
similarity index 100%
rename from blipblop.py
rename to blipblop_main.py
diff --git a/blipblop_win.spec b/blipblop_win.spec
new file mode 100644
index 0000000..cbe9f7d
--- /dev/null
+++ b/blipblop_win.spec
@@ -0,0 +1,49 @@
+# -*- mode: python ; coding: utf-8 -*-
+
+block_cipher = None
+
+
+a = Analysis(['blipblop_main.py'],
+             pathex=['.'],
+             binaries=[],
+             datas=[('docs/index.md', "docs"), 
+                    ('docs/visual_task.md', "docs"), 
+                    ('docs/auditory_task.md', "docs"),
+                    ('docs/license.md', "docs"),
+                    ('docs/tasks.md', "docs"),
+                    ('docs/images/blipblop_main.png', "docs/images"),
+                    ('docs/images/blipblop_logo.png', "docs/images"),
+                    ('sounds/bell.wav', "sounds"),
+                    ('sounds/complete.wav', "sounds"),
+                    ('sounds/message.wav', "sounds"),
+                    ('icons/blipblop_logo.ico', '.')
+                    ],
+             hiddenimports=[],
+             hookspath=[],
+             runtime_hooks=[],
+             excludes=[],
+             win_no_prefer_redirects=False,
+             win_private_assemblies=False,
+             cipher=block_cipher,
+             noarchive=False)
+pyz = PYZ(a.pure, a.zipped_data,
+             cipher=block_cipher)
+
+exe = EXE(pyz,
+          a.scripts,
+          a.binaries,
+          a.zipfiles,
+          a.datas,
+          [],
+          exclude_binaries=True,
+          name='BlipBlop',
+          debug=True,
+          bootloader_ignore_signals=False,
+          strip=False,
+          upx=True,
+          console=True,
+          upx_exclude=[],
+          runtime_tmpdir=None,
+          icon='icons/blipblop_logo.ico'
+          )
+
diff --git a/docs/auditory_task.md b/docs/auditory_task.md
new file mode 100644
index 0000000..f9c53e2
--- /dev/null
+++ b/docs/auditory_task.md
@@ -0,0 +1,2 @@
+# Auditory reaction times
+
diff --git a/docs/images/blipblop_logo.png b/docs/images/blipblop_logo.png
new file mode 100644
index 0000000..9d38447
Binary files /dev/null and b/docs/images/blipblop_logo.png differ
diff --git a/docs/images/blipblop_main.png b/docs/images/blipblop_main.png
new file mode 100644
index 0000000..c442c1f
Binary files /dev/null and b/docs/images/blipblop_main.png differ
diff --git a/docs/index.md b/docs/index.md
index 8bb5014..f53d5e8 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,4 +1,27 @@
+
 # BlipBlop
 
-Tiny tool for measuring reaction times upon auditory or visual stimuli.
+![blip blop](images/blipblop_logo.png) 
+
+BlipBlop is a tiny tool for measuring reaction times upon auditory or visual stimulation. This tool is open-source. The source code can be found on [GitHub](https://github.com/jgrewe/blipblop). It is published under the MIT open source [license](license.md). 
+Auditory stimuli have been taken from the freedesktop system sounds [freedesktop](https://freedesktop.org)
+
+
+## Main Window
+
+![main screen](images/blipblop_main.png) 
+
+
+### Visual task
+
+Measure reaction times to a visual stimulus that pops in the center of the screen. ([more](visual_task.md))
+
+### Auditory task
+
+Measure reaction times to a auditory stimulus. ([more](auditory_task.md))
+
+### Results View
+
+A simple tablular view that shows the measured reaction times. The data is given in seconds. Select the data and press (**ctrl+c** or **cmd+c**) to copy the selection to clipboard and paste it into your favorite spreadsheet tool for further analysis.
+
 
diff --git a/docs/license.md b/docs/license.md
new file mode 100644
index 0000000..4c57d76
--- /dev/null
+++ b/docs/license.md
@@ -0,0 +1,9 @@
+# License
+
+Copyright 2021, Jan Grewe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/docs/results.md b/docs/results.md
new file mode 100644
index 0000000..e43f7df
--- /dev/null
+++ b/docs/results.md
@@ -0,0 +1 @@
+# Results view
\ No newline at end of file
diff --git a/docs/tasks.md b/docs/tasks.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/visual_task.md b/docs/visual_task.md
new file mode 100644
index 0000000..5c7035c
--- /dev/null
+++ b/docs/visual_task.md
@@ -0,0 +1,2 @@
+# Visual reaction times
+
diff --git a/resources.qrc b/resources.qrc
index 9f198f3..feb0ca0 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -2,6 +2,7 @@
 <RCC version="1.0">
     <qresource prefix="icons">
         <file alias="app_icon">icons/blipblop_logo.icns</file>
+        <file alias="app_icon_png">icons/blipblop_logo.png</file>
         <file alias="blipblop_logo">icons/blipblop_logo.png</file>
         <file alias="visual_task">icons/visual_task.png</file>
         <file alias="visual_task_large">icons/visual_task_large.png</file>
@@ -11,17 +12,16 @@
         <file alias="docs_back">icons/back_btn.png</file>
         <file alias="docs_forward">icons/fwd_btn.png</file>
         <file alias="help">icons/help.png</file>
+        <file alias="quit">icons/blipblop_quit.png</file>
         <file alias="new_session">icons/new_session.png</file>
         <file alias="new_session_larg">icons/new_session_large.png</file>
         <file alias="results_table">icons/blipblop_table.png</file>
         <file alias="settings">icons/settings.png</file>
     </qresource>
-    <qresource prefix="sounds">
-        <file alias="bell">sounds/bell.wav</file>
-        <file alias="message">sounds/message.wav</file>
-        <file alias="complete">sounds/complete.wav</file>
-    </qresource>
     <qresource prefix="docs">
         <file alias="index">docs/index.md</file>
+        <file alias="visual_task">docs/visual_task.md</file>
+        <file alias="auditory_task">docs/auditory_task.md</file>
+        <file alias="license">docs/license.md</file>
     </qresource>
 </RCC>
\ No newline at end of file