From c026d9bfc5f8bccb869b41e28c42aad5d83fae19 Mon Sep 17 00:00:00 2001
From: P-A Loizeau <p.-a.loizeau@gsi.de>
Date: Mon, 20 Mar 2023 14:05:48 +0100
Subject: [PATCH] [Evt disp] Add event display for CbmTimeslice with CbmEvent
 array (squashed)

- New classes to draw PixelHit depending on (Timeslice, Event) instead of only TreeEntry (=Timeslice in this case)
- New classes to draw CbmGlobalTrack depending on (Timeslice, Event) instead of only TreeEntry (=Timeslice in this case)
- New CbmTimesliceManager based on FairEventManager + corresponding Editor for the event display GUI
  - Choice of timeslice with safety check
  - Choice of event in timeslice with safety check
  - Calls to GotoEvent methods of new PixelHit and GlobalTrack drawing classes
  - Pre-sets of Track & PixelHit for sis100e, sis100m and mCBM
  - Add buttons for next and prev TS in new display
  - Add views tunned for mCBM in new display
  - Add support for visibility tuning in xml config + mcbm example
- Example macro for mCBM 2022 reco
  - Add support for align matrices + make unp file optional
  - Example xml file for geometry view config, tuned for run 2391 and the Nickel setup
- Example macro for CBM
  - Example xml file for geometry view config, tuned for the current SIS100 electron setup
- Decouple TS evt display manager from FairEventManager + rel. fixes
  - Remove inheritance of CbmTimesliceManager from FairEventManager + clone (and cleanup) all required blocks of code from it
  - Clone FairEveTransparencyControl to CbmTsEveTransparencyControl as only gui element calling FairEventManager instance
  - Remove call to FairEventManager instance to CbmTimesliceManager one in all Timeslice event display classes
  - Fix calls to wrong methods in CbmTimeslicePixelHitSetDraw
  - Fix printout of TS and Event info in main views
- Add support for animation and screenshots + fixes
---
 core/eventdisplay/CMakeLists.txt              |   6 +
 core/eventdisplay/CbmDisplayLinkDef.h         |   9 +-
 core/eventdisplay/CbmTimesliceManager.cxx     | 874 ++++++++++++++++++
 core/eventdisplay/CbmTimesliceManager.h       | 171 ++++
 .../CbmTimesliceManagerEditor.cxx             | 464 ++++++++++
 core/eventdisplay/CbmTimesliceManagerEditor.h |  92 ++
 .../CbmTimeslicePixelHitSetDraw.cxx           | 157 ++++
 .../CbmTimeslicePixelHitSetDraw.h             |  42 +
 core/eventdisplay/CbmTimesliceRecoTracks.cxx  | 250 +++++
 core/eventdisplay/CbmTimesliceRecoTracks.h    |  91 ++
 .../eventdisplay/CbmTsEveAnimationControl.cxx | 264 ++++++
 core/eventdisplay/CbmTsEveAnimationControl.h  | 129 +++
 .../CbmTsEveTransparencyControl.cxx           |  52 ++
 .../CbmTsEveTransparencyControl.h             |  37 +
 macro/.gitignore                              |   2 +
 .../beamtime/mcbm2022/event_display_l1reco.C  | 104 +++
 ..._disp_conf_mcbm_beam_2022_05_23_nickel.xml |  48 +
 macro/run/event_display_reco_ts.C             |  89 ++
 macro/run/evt_disp_conf_cbm_sis100e.xml       |  46 +
 19 files changed, 2924 insertions(+), 3 deletions(-)
 create mode 100644 core/eventdisplay/CbmTimesliceManager.cxx
 create mode 100644 core/eventdisplay/CbmTimesliceManager.h
 create mode 100644 core/eventdisplay/CbmTimesliceManagerEditor.cxx
 create mode 100644 core/eventdisplay/CbmTimesliceManagerEditor.h
 create mode 100644 core/eventdisplay/CbmTimeslicePixelHitSetDraw.cxx
 create mode 100644 core/eventdisplay/CbmTimeslicePixelHitSetDraw.h
 create mode 100644 core/eventdisplay/CbmTimesliceRecoTracks.cxx
 create mode 100644 core/eventdisplay/CbmTimesliceRecoTracks.h
 create mode 100644 core/eventdisplay/CbmTsEveAnimationControl.cxx
 create mode 100644 core/eventdisplay/CbmTsEveAnimationControl.h
 create mode 100644 core/eventdisplay/CbmTsEveTransparencyControl.cxx
 create mode 100644 core/eventdisplay/CbmTsEveTransparencyControl.h
 create mode 100644 macro/.gitignore
 create mode 100644 macro/beamtime/mcbm2022/event_display_l1reco.C
 create mode 100644 macro/beamtime/mcbm2022/evt_disp_conf_mcbm_beam_2022_05_23_nickel.xml
 create mode 100644 macro/run/event_display_reco_ts.C
 create mode 100644 macro/run/evt_disp_conf_cbm_sis100e.xml

diff --git a/core/eventdisplay/CMakeLists.txt b/core/eventdisplay/CMakeLists.txt
index 2457d027c4..7efa64413e 100644
--- a/core/eventdisplay/CMakeLists.txt
+++ b/core/eventdisplay/CMakeLists.txt
@@ -10,6 +10,12 @@ set(SRCS
   CbmPointSetArray.cxx
   CbmPointSetArrayDraw.cxx
   CbmPointSetArrayEditor.cxx
+  CbmTimesliceManager.cxx
+  CbmTimesliceManagerEditor.cxx
+  CbmTimeslicePixelHitSetDraw.cxx
+  CbmTimesliceRecoTracks.cxx
+  CbmTsEveAnimationControl.cxx
+  CbmTsEveTransparencyControl.cxx
   )
 
 
diff --git a/core/eventdisplay/CbmDisplayLinkDef.h b/core/eventdisplay/CbmDisplayLinkDef.h
index f9fb286cae..2503c538ee 100644
--- a/core/eventdisplay/CbmDisplayLinkDef.h
+++ b/core/eventdisplay/CbmDisplayLinkDef.h
@@ -2,8 +2,6 @@
    SPDX-License-Identifier: GPL-3.0-only
    Authors: Florian Uhlig [committer] */
 
-// $Id: TrdLinkDef.h,v 1.10 2006/06/20 09:39:59 kresan Exp $
-
 #ifdef __CINT__
 
 #pragma link off all globals;
@@ -17,5 +15,10 @@
 #pragma link C++ class CbmPointSetArray;
 #pragma link C++ class CbmPointSetArrayDraw;
 #pragma link C++ class CbmPointSetArrayEditor;
-
+#pragma link C++ class CbmTimesliceManager;
+#pragma link C++ class CbmTimesliceManagerEditor;
+#pragma link C++ class CbmTimeslicePixelHitSetDraw;
+#pragma link C++ class CbmTimesliceRecoTracks;
+#pragma link C++ class CbmTsEveAnimationControl;
+#pragma link C++ class CbmTsEveTransparencyControl;
 #endif
diff --git a/core/eventdisplay/CbmTimesliceManager.cxx b/core/eventdisplay/CbmTimesliceManager.cxx
new file mode 100644
index 0000000000..dc8bdfdeb0
--- /dev/null
+++ b/core/eventdisplay/CbmTimesliceManager.cxx
@@ -0,0 +1,874 @@
+/* Copyright (C) 2023 Facility for Antiproton and Ion Research in Europe, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau[committer] */
+
+#include "CbmTimesliceManager.h"
+
+#include "CbmEvent.h"                     // For CbmEvent
+#include "CbmTimeslicePixelHitSetDraw.h"  // For CbmTimeslicePixelHitSetDraw
+#include "CbmTimesliceRecoTracks.h"       // For CbmTimesliceRecoTracks
+
+#include "FairRootManager.h"  // for FairRootManager
+#include "FairTask.h"         // for FairTask
+#include "FairXMLNode.h"      // for FairXMLNode and FairXMLFile
+
+#include <TClonesArray.h>  // for TClonesArray
+#include <TDatabasePDG.h>  // for TDatabasePDG
+#include <TEveBrowser.h>
+#include <TEveGeoNode.h>  // for TEveGeoTopNode
+#include <TEveManager.h>  // for TEveManager, gEve
+#include <TEveProjectionManager.h>
+#include <TEveProjections.h>  // for TEveProjection, TEveProjection::k...
+#include <TEveScene.h>
+#include <TEveText.h>
+#include <TEveTrans.h>
+#include <TEveViewer.h>
+#include <TEveWindow.h>  // for TEveWindowPack, TEveWindowSlot
+#include <TGFileDialog.h>
+#include <TGLAnnotation.h>
+#include <TGLCameraOverlay.h>
+#include <TGLClip.h>  // for TGLClip, TGLClip::kClipPlane, TGL...
+#include <TGLFontManager.h>
+#include <TGLLightSet.h>
+#include <TGLViewer.h>
+#include <TGeoBBox.h>
+#include <TGeoManager.h>  // for gGeoManager, TGeoManager
+#include <TGeoNode.h>
+#include <TGeoVolume.h>  // for TGeoVolume
+#include <TVector3.h>
+
+// +++++++++++++++++++++++++++++++++++++++++++++ Public +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
+CbmTimesliceManager* CbmTimesliceManager::gRinstanceTsMan = 0;
+
+CbmTimesliceManager* CbmTimesliceManager::Instance() { return gRinstanceTsMan; }
+
+CbmTimesliceManager::CbmTimesliceManager() : TEveEventManager("CbmTimesliceManager", "")
+{
+  /// Initialize instance pointer with this object when create directly
+  gRinstanceTsMan = this;
+
+  fRunAna      = FairRunAna::Instance();
+  fRootManager = FairRootManager::Instance();
+
+  AddParticlesToPdgDataBase();
+  InitPdgColorMap();
+}
+
+void CbmTimesliceManager::SetDisplayCbmElectron()
+{
+  CbmTimeslicePixelHitSetDraw* drawStsHit  = new CbmTimeslicePixelHitSetDraw("StsHit", kBlue, kFullSquare);
+  CbmTimeslicePixelHitSetDraw* drawRichHit = new CbmTimeslicePixelHitSetDraw("RichHit", kCyan, kFullSquare);
+  CbmTimeslicePixelHitSetDraw* drawTrdHit  = new CbmTimeslicePixelHitSetDraw("TrdHit", kYellow, kFullSquare);
+  CbmTimeslicePixelHitSetDraw* drawTofHit  = new CbmTimeslicePixelHitSetDraw("TofHit", kRed, kFullSquare);
+  CbmTimeslicePixelHitSetDraw* drawPsdHit  = new CbmTimeslicePixelHitSetDraw("PsdHit", kOrange, kFullSquare);
+
+  CbmTimesliceRecoTracks* drawTrack = new CbmTimesliceRecoTracks();
+
+  AddTask(drawStsHit);
+  AddTask(drawRichHit);
+  AddTask(drawTrdHit);
+  AddTask(drawTofHit);
+  AddTask(drawPsdHit);
+
+  AddTask(drawTrack);
+}
+
+void CbmTimesliceManager::SetDisplayCbmMuon()
+{
+  CbmTimeslicePixelHitSetDraw* drawMvdHit  = new CbmTimeslicePixelHitSetDraw("MvdHit", kBlue, kFullSquare);
+  CbmTimeslicePixelHitSetDraw* drawStsHit  = new CbmTimeslicePixelHitSetDraw("StsHit", kBlue, kFullSquare);
+  CbmTimeslicePixelHitSetDraw* drawMuchHit = new CbmTimeslicePixelHitSetDraw("MuchHit", kCyan, kFullSquare);
+  CbmTimeslicePixelHitSetDraw* drawTrdHit  = new CbmTimeslicePixelHitSetDraw("TrdHit", kYellow, kFullSquare);
+  CbmTimeslicePixelHitSetDraw* drawTofHit  = new CbmTimeslicePixelHitSetDraw("TofHit", kRed, kFullSquare);
+  CbmTimeslicePixelHitSetDraw* drawPsdHit  = new CbmTimeslicePixelHitSetDraw("PsdHit", kOrange, kFullSquare);
+
+  AddTask(drawMvdHit);
+  AddTask(drawStsHit);
+  AddTask(drawMuchHit);
+  AddTask(drawTrdHit);
+  AddTask(drawTofHit);
+  AddTask(drawPsdHit);
+
+  CbmTimesliceRecoTracks* drawTrack = new CbmTimesliceRecoTracks();
+  AddTask(drawTrack);
+}
+
+void CbmTimesliceManager::SetDisplayMcbm()
+{
+  CbmTimeslicePixelHitSetDraw* drawStsHit = new CbmTimeslicePixelHitSetDraw("StsHit", kBlue, kFullSquare);
+  //  CbmTimeslicePixelHitSetDraw* drawMuchHit     = new CbmTimeslicePixelHitSetDraw("MuchHit", kCyan, kFullSquare);
+  CbmTimeslicePixelHitSetDraw* drawTrdHit  = new CbmTimeslicePixelHitSetDraw("TrdHit", kYellow, kFullSquare);
+  CbmTimeslicePixelHitSetDraw* drawTofHit  = new CbmTimeslicePixelHitSetDraw("TofHit", kRed, kFullSquare);
+  CbmTimeslicePixelHitSetDraw* drawRichHit = new CbmTimeslicePixelHitSetDraw("RichHit", kOrange, kFullSquare);
+
+  AddTask(drawStsHit);
+  //AddTask(drawMuchHit);
+  AddTask(drawTrdHit);
+  AddTask(drawTofHit);
+  AddTask(drawRichHit);
+
+  CbmTimesliceRecoTracks* drawTrack = new CbmTimesliceRecoTracks();
+  AddTask(drawTrack);
+
+  fbMcbmViewersEna = true;
+}
+
+void CbmTimesliceManager::SetTransparency(Bool_t use_xml, Int_t trans)
+{
+  if (use_xml == kFALSE) {
+    // high transparency
+    Int_t vis_level = gGeoManager->GetVisLevel();
+    TGeoNode* top   = gGeoManager->GetTopNode();
+    SetTransparencyForLayer(top, vis_level, trans);
+  }
+  else {
+    // normal transparency
+    if (fXMLConfig != "") {  //
+      LoadXMLSettings();
+    }
+    else {
+      Int_t vis_level = gGeoManager->GetVisLevel();
+      TGeoNode* top   = gGeoManager->GetTopNode();
+      SetTransparencyForLayer(top, vis_level, 0);
+    }
+  }
+  if (gEve->GetGlobalScene()->GetRnrState()) {
+    gEve->GetGlobalScene()->SetRnrState(kFALSE);
+    gEve->GetGlobalScene()->SetRnrState(kTRUE);
+    gEve->Redraw3D();
+  }
+}
+
+void CbmTimesliceManager::SwitchBackground(Bool_t /*light*/)
+{
+  /// PAL 31/05/2023: No parameter possible to SwitchColorSet of TEveViewerList in recent root versions !?!
+  gEve->GetViewers()->SwitchColorSet();
+}
+
+void CbmTimesliceManager::Init(Int_t visopt, Int_t vislvl, Int_t maxvisnds)
+{
+  TEveManager::Create();
+  fRunAna->Init();
+
+  if (!InitializeMainView(visopt, vislvl, maxvisnds)) {
+    LOG(fatal) << "CbmTimesliceManager::Init() => Failed main view initialization";
+  }
+
+  if (fbMcbmViewersEna) {  //
+    InitializeViewsMcbm();
+  }
+  else {
+    InitializeViewsCbm();
+  }
+
+  fCbmEvents = dynamic_cast<TClonesArray*>(FairRootManager::Instance()->GetObject("CbmEvent"));
+
+  if (nullptr == fCbmEvents) {
+    LOG(fatal) << "CbmTimesliceManager::Init() => CbmEvents branch not found! Task will be deactivated";
+  }
+}
+
+void CbmTimesliceManager::Open() {}
+void CbmTimesliceManager::UpdateEditor() {}
+void CbmTimesliceManager::Close() {}
+void CbmTimesliceManager::DisplaySettings() {}
+
+Int_t CbmTimesliceManager::Color(int pdg)
+{
+  if (fPDGToColor.find(pdg) != fPDGToColor.end()) {  //
+    return fPDGToColor[pdg];
+  }
+  return 0;
+}
+
+void CbmTimesliceManager::GotoTimeslice(uint32_t event)
+{
+  fTimesliceIdx = event;
+  /// This will force all added tasks to load first event of this TS if possible
+  FairRunAna::Instance()->Run(static_cast<Long64_t>(fTimesliceIdx));
+}
+
+void CbmTimesliceManager::NextTimeslice()
+{
+  /// Check if possible (min/max)
+
+  /// Re-use main method
+  GotoTimeslice(GetCurrentTimeslice() + 1);
+}
+
+void CbmTimesliceManager::PrevTimeslice()
+{
+  /// Check if possible (min/max)
+
+  /// Re-use main method
+  GotoTimeslice(GetCurrentTimeslice() - 1);
+}
+
+void CbmTimesliceManager::GotoEvent(uint32_t event)
+{
+  fEventIdx = event;
+  /// Get List of tasks from FairRunAna
+  TList* taskList = FairRunAna::Instance()->GetMainTask()->GetListOfTasks();
+
+  /// Tell each of them to go to selected event
+  for (TObject* task : *taskList) {
+    if (nullptr != dynamic_cast<CbmTimesliceRecoTracks*>(task)) {
+      dynamic_cast<CbmTimesliceRecoTracks*>(task)->GotoEvent(event);
+    }
+    else if (nullptr != dynamic_cast<CbmTimeslicePixelHitSetDraw*>(task)) {
+      dynamic_cast<CbmTimeslicePixelHitSetDraw*>(task)->GotoEvent(event);
+    }
+  }
+}
+
+void CbmTimesliceManager::NextEvent()
+{
+  /// Check if possible (min/max)
+
+  /// Re-use main method
+  GotoEvent(fEventIdx + 1);
+}
+
+void CbmTimesliceManager::PrevEvent()
+{
+  /// Check if possible (min/max)
+
+  /// Re-use main method
+  GotoEvent(fEventIdx - 1);
+}
+
+double_t CbmTimesliceManager::GetTimesliceTime()
+{
+  fTimeTimeslice = FairRootManager::Instance()->GetEventTime();
+  return fTimeTimeslice;
+}
+
+double_t CbmTimesliceManager::GetEventTime()
+{
+  const CbmEvent* event = dynamic_cast<const CbmEvent*>(fCbmEvents->At(fEventIdx));
+  fTimeEvent            = event->GetTzero();
+  return fTimeEvent;
+}
+
+void CbmTimesliceManager::SetTsTimeText(Double_t time)
+{
+  TString stime;
+  stime.Form("TS Time: %.2f", time);
+  stime += " ns";
+  fTimesliceTimeText->SetText(stime);
+}
+
+void CbmTimesliceManager::SetTsNumberText(Int_t evtNumber)
+{
+  TString text = "TS: ";
+  text += evtNumber;
+  fTimesliceNumberText->SetText(text);
+}
+
+void CbmTimesliceManager::SetEvtTimeText(Double_t time)
+{
+  TString stime;
+  stime.Form("Event Time: %.2f", time);
+  stime += " ns";
+  fEventTimeText->SetText(stime);
+}
+
+void CbmTimesliceManager::SetEvtNumberText(Int_t evtNumber)
+{
+  TString text = "Event: ";
+  text += evtNumber;
+  fEventNumberText->SetText(text);
+}
+
+void CbmTimesliceManager::MakeScreenshot(CbmTsEveAnimationControl::eScreenshotType screenshotType, TString path)
+{
+  TString filename;
+  if (path == "") {
+    const char* filetypes[] = {"PNG", "*.png", "JPG", "*.jpg", 0, 0};
+    TGFileInfo fi;
+    fi.fFileTypes = filetypes;
+    fi.fIniDir    = StrDup(".");
+    new TGFileDialog(gClient->GetRoot(), gEve->GetMainWindow(), kFDSave, &fi);
+    if (fi.fFilename == nullptr) {  //
+      return;
+    }
+    filename = fi.fFilename;
+  }
+  else {
+    filename = path;
+  }
+
+  if (fbMcbmViewersEna) {
+    switch (screenshotType) {
+      case CbmTsEveAnimationControl::eScreenshotType::k3D: {
+        gEve->GetDefaultGLViewer()->SavePicture(filename);
+        break;
+      }
+      case CbmTsEveAnimationControl::eScreenshotType::kZX: {
+        TGLViewer* gl = fViewZX->GetGLViewer();
+        gl->SavePicture(filename);
+        break;
+      }
+      case CbmTsEveAnimationControl::eScreenshotType::kZY: {
+        TGLViewer* gl = fViewZY->GetGLViewer();
+        gl->SavePicture(filename);
+        break;
+      }
+      case CbmTsEveAnimationControl::eScreenshotType::kAll: {
+        TString filename_path = filename(0, filename.Last('.'));
+        TString filename_ext  = filename(filename.Last('.') + 1, 3);
+        TString filename3d    = Form("%s_3d.%s", filename_path.Data(), filename_ext.Data());
+        TString filenameZY    = Form("%s_ZY.%s", filename_path.Data(), filename_ext.Data());
+        TString filenameZX    = Form("%s_ZX.%s", filename_path.Data(), filename_ext.Data());
+        gEve->GetDefaultGLViewer()->SavePicture(filename3d);
+        TGLViewer* gl = fViewZY->GetGLViewer();
+        gl->SavePicture(filenameZY);
+        gl = fViewZX->GetGLViewer();
+        gl->SavePicture(filenameZX);
+        break;
+      }
+      default:
+        /// Type missmatch, should never happen if Comboboxes properly set but better safe than sorry
+        break;
+    }
+  }
+  else {
+    switch (screenshotType) {
+      case CbmTsEveAnimationControl::eScreenshotType::k3D: {
+        gEve->GetDefaultGLViewer()->SavePicture(filename);
+        break;
+      }
+      case CbmTsEveAnimationControl::eScreenshotType::kXY: {
+        TGLViewer* gl = fRPhiView->GetGLViewer();
+        gl->SavePicture(filename);
+        break;
+      }
+      case CbmTsEveAnimationControl::eScreenshotType::kZ: {
+        TGLViewer* gl = fRhoZView->GetGLViewer();
+        gl->SavePicture(filename);
+        break;
+      }
+      case CbmTsEveAnimationControl::eScreenshotType::kAll: {
+        TString filename_path = filename(0, filename.Last('.'));
+        TString filename_ext  = filename(filename.Last('.') + 1, 3);
+        TString filename3d    = Form("%s_3d.%s", filename_path.Data(), filename_ext.Data());
+        TString filenameRphi  = Form("%s_XY.%s", filename_path.Data(), filename_ext.Data());
+        TString filenameRhoz  = Form("%s_Z.%s", filename_path.Data(), filename_ext.Data());
+        gEve->GetDefaultGLViewer()->SavePicture(filename3d);
+        TGLViewer* gl = fRPhiView->GetGLViewer();
+        gl->SavePicture(filenameRphi);
+        gl = fRhoZView->GetGLViewer();
+        gl->SavePicture(filenameRhoz);
+        break;
+      }
+      default:
+        /// Type missmatch, should never happen if Comboboxes properly set but better safe than sorry
+        break;
+    }
+  }
+}
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
+
+// ++++++++++++++++++++++++++++++++++++++++++++ Protected +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
+void CbmTimesliceManager::LoadXMLSettings()
+{
+  /// Complement the FairEventManager with a new keyword for setting the node visibility (+ daughters if recursive > 0)
+  FairXMLFile xmlfile(fXMLConfig, "read");
+  FairXMLNode* xml = xmlfile.GetRootNode();
+  for (int i = 0; i < xml->GetNChildren(); i++) {
+    TString nodename = xml->GetChild(i)->GetName();
+    if (nodename.EqualTo("Detectors")) {
+      TGeoNode* top        = gGeoManager->GetTopNode();
+      FairXMLNode* top_xml = xml->GetChild(i)->GetChild(0);
+      if (top_xml != nullptr) {  //
+        LoadXMLDetector(top, top_xml);
+      }
+    }
+    else if (nodename.EqualTo("MCTracksColors")) {
+      FairXMLNode* colors = xml->GetChild(i);
+      for (int j = 0; j < colors->GetNChildren(); j++) {
+        FairXMLNode* color = colors->GetChild(j);
+        TString pgd_code   = color->GetAttrib("pdg")->GetValue();
+        TString color_code = color->GetAttrib("color")->GetValue();
+        //fPDGToColor[pgd_code.Atoi()] = StringToColor(color_code);
+      }
+    }
+  }
+  if (gEve->GetGlobalScene()->GetRnrState()) {
+    gEve->GetGlobalScene()->SetRnrState(kFALSE);
+    gEve->GetGlobalScene()->SetRnrState(kTRUE);
+    gEve->Redraw3D();
+  }
+}
+
+void CbmTimesliceManager::LoadXMLDetector(TGeoNode* node, FairXMLNode* xml, Int_t depth)
+{
+  /// Complement the FairEventManager with a new keyword for setting the node visibility (+ daughters if recursive > 0)
+  TString name      = xml->GetAttrib("name")->GetValue();
+  TString node_name = node->GetName();
+  Bool_t recursive  = (xml->GetAttrib("recursive")->GetValue().Length() != 0 && !name.EqualTo(node_name));
+  if (recursive && depth == 0) {  //
+    return;
+  }
+  TString transparency  = xml->GetAttrib("transparency")->GetValue();
+  TString color         = xml->GetAttrib("color")->GetValue();
+  TString visibility    = xml->GetAttrib("visibility")->GetValue();
+  TString recursive_val = xml->GetAttrib("recursive")->GetValue();
+  if (!recursive && "0" != recursive_val) {  //
+    LOG(info) << "LoadXMLDetector called for " << node_name;
+  }
+  if (!color.EqualTo("")) {
+    node->GetVolume()->SetFillColor(StringToColor(color));
+    node->GetVolume()->SetLineColor(StringToColor(color));
+  }
+  if (!transparency.EqualTo("")) {  //
+    node->GetVolume()->SetTransparency((Char_t)(transparency.Atoi()));
+  }
+  if (!visibility.EqualTo("")) {
+    bool bVisVal = (0 < visibility.Atoi());
+    node->GetVolume()->SetVisibility(bVisVal);
+    if (!recursive) {                                                                //
+      LOG(info) << "Setting " << node_name << (bVisVal ? " Visible" : " Invisible")  //
+                << ("0" != recursive_val ? " and its daughters also" : "");
+    }
+  }
+  if (recursive_val.Length() > 0) {
+    Int_t xml_depth = recursive_val.Atoi();
+    /*
+    /// Original FairRoot counting, led to troubles in my case
+    if (recursive) {
+        xml_depth = depth - 1;
+    }
+    */
+    if (0 < xml_depth) {
+      for (int i = 0; i < node->GetNdaughters(); i++) {
+        TGeoNode* daughter_node = node->GetDaughter(i);
+        LoadXMLDetector(daughter_node, xml, xml_depth);
+      }
+    }
+  }
+  if (xml->GetNChildren() > 0 && !recursive) {
+    for (int i = 0; i < node->GetNdaughters(); i++) {
+      TString subdetector_name = node->GetDaughter(i)->GetName();
+      for (int j = 0; j < xml->GetNChildren(); j++) {
+        FairXMLNode* subnode = xml->GetChild(j);
+        TString subnode_name = subnode->GetAttrib("name")->GetValue();
+        if (subnode_name == subdetector_name) {  //
+          LoadXMLDetector(node->GetDaughter(i), subnode);
+        }
+      }
+    }
+  }
+}
+
+Int_t CbmTimesliceManager::StringToColor(TString color) const
+{
+  if (color.Contains("k")) {
+    Int_t plus_index  = color.First('+');
+    Int_t minus_index = color.First('-');
+    Int_t cut         = plus_index;
+    if (cut == -1) {  //
+      cut = minus_index;
+    }
+    if (cut == -1) {  //
+      cut = color.Length();
+    }
+    TString col_name(color(0, cut));
+    Int_t col_val = 0;
+    if (col_name.EqualTo("kWhite")) {  //
+      col_val = 0;
+    }
+    else if (col_name.EqualTo("kBlack")) {
+      col_val = 1;
+    }
+    else if (col_name.EqualTo("kGray")) {
+      col_val = 920;
+    }
+    else if (col_name.EqualTo("kRed")) {
+      col_val = 632;
+    }
+    else if (col_name.EqualTo("kGreen")) {
+      col_val = 416;
+    }
+    else if (col_name.EqualTo("kBlue")) {
+      col_val = 600;
+    }
+    else if (col_name.EqualTo("kYellow")) {
+      col_val = 400;
+    }
+    else if (col_name.EqualTo("kMagenta")) {
+      col_val = 616;
+    }
+    else if (col_name.EqualTo("kCyan")) {
+      col_val = 432;
+    }
+    else if (col_name.EqualTo("kOrange")) {
+      col_val = 800;
+    }
+    else if (col_name.EqualTo("kSpring")) {
+      col_val = 820;
+    }
+    else if (col_name.EqualTo("kTeal")) {
+      col_val = 840;
+    }
+    else if (col_name.EqualTo("kAzure")) {
+      col_val = 860;
+    }
+    else if (col_name.EqualTo("kViolet")) {
+      col_val = 880;
+    }
+    else if (col_name.EqualTo("kPink")) {
+      col_val = 900;
+    }
+    TString col_num(color(cut + 1, color.Length()));
+    if (col_num.Length() > 0) {   //
+      if (color.Contains("+")) {  //
+        col_val += col_num.Atoi();
+      }
+      else {
+        col_val -= col_num.Atoi();
+      }
+    }
+    return col_val;
+  }
+  else {
+    return color.Atoi();
+  }
+}
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
+
+// +++++++++++++++++++++++++++++++++++++++++++++ Private ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
+void CbmTimesliceManager::AddParticlesToPdgDataBase()
+{
+  // Add particles to the PDG data base
+  TDatabasePDG* pdgDB = TDatabasePDG::Instance();
+
+  const Double_t kAu2Gev   = 0.9314943228;
+  const Double_t khSlash   = 1.0545726663e-27;
+  const Double_t kErg2Gev  = 1 / 1.6021773349e-3;
+  const Double_t khShGev   = khSlash * kErg2Gev;
+  const Double_t kYear2Sec = 3600 * 24 * 365.25;
+
+  // Ions
+  if (!pdgDB->GetParticle(1000010020)) {  //
+    pdgDB->AddParticle("Deuteron", "Deuteron", 2 * kAu2Gev + 8.071e-3, kTRUE, 0, 3, "Ion", 1000010020);
+  }
+
+  if (!pdgDB->GetParticle(1000010030)) {  //
+    pdgDB->AddParticle("Triton", "Triton", 3 * kAu2Gev + 14.931e-3, kFALSE, khShGev / (12.33 * kYear2Sec), 3, "Ion",
+                       1000010030);
+  }
+
+  if (!pdgDB->GetParticle(1000020040)) {  //
+    pdgDB->AddParticle("Alpha", "Alpha", 4 * kAu2Gev + 2.424e-3, kTRUE, khShGev / (12.33 * kYear2Sec), 6, "Ion",
+                       1000020040);
+  }
+
+  if (!pdgDB->GetParticle(1000020030)) {  //
+    pdgDB->AddParticle("He3", "He3", 3 * kAu2Gev + 14.931e-3, kFALSE, 0, 6, "Ion", 1000020030);
+  }
+
+  // Special particles
+  if (!pdgDB->GetParticle(50000050)) {  //
+    pdgDB->AddParticle("Cherenkov", "Cherenkov", 0, kFALSE, 0, 0, "Special", 50000050);
+  }
+
+  if (!pdgDB->GetParticle(50000051)) {  //
+    pdgDB->AddParticle("FeedbackPhoton", "FeedbackPhoton", 0, kFALSE, 0, 0, "Special", 50000051);
+  }
+}
+
+void CbmTimesliceManager::InitPdgColorMap()
+{
+  fPDGToColor[22]    = 623;  // photon
+  fPDGToColor[-2112] = 2;    // anti-neutron
+  fPDGToColor[-11]   = 3;    // e+
+  fPDGToColor[-3122] = 4;    // anti-lambda
+  fPDGToColor[11]    = 5;    // e-
+  fPDGToColor[-3222] = 6;    // Sigma -
+  fPDGToColor[12]    = 7;    // e-neutrino
+  fPDGToColor[-3212] = 8;    //  Sigma0
+  fPDGToColor[-13]   = 9;    // mu+
+  fPDGToColor[-3112] = 10;   // Sigma+ (PB
+  fPDGToColor[13]    = 11;   //  mu-
+  fPDGToColor[-3322] = 12;   //  Xi0
+  fPDGToColor[111]   = 13;   // pi0
+  fPDGToColor[-3312] = 14;   //  Xi+
+  fPDGToColor[211]   = 15;   // pi+
+  fPDGToColor[-3334] = 16;   //  Omega+ (PB)
+  fPDGToColor[-211]  = 17;   // pi-
+  fPDGToColor[-15]   = 18;   // tau+
+  fPDGToColor[130]   = 19;   // K long
+  fPDGToColor[15]    = 20;   //  tau -
+  fPDGToColor[321]   = 21;   // K+
+  fPDGToColor[411]   = 22;   // D+
+  fPDGToColor[-321]  = 23;   // K-
+  fPDGToColor[-411]  = 24;   // D-
+  fPDGToColor[2112]  = 25;   // n
+  fPDGToColor[421]   = 26;   // D0
+  fPDGToColor[2212]  = 27;   // p
+  fPDGToColor[-421]  = 28;   // D0
+  fPDGToColor[-2212] = 29;   //  anti-proton
+  fPDGToColor[431]   = 30;   // Ds+
+  fPDGToColor[310]   = 31;   // K short
+  fPDGToColor[-431]  = 32;   // anti Ds-
+  fPDGToColor[221]   = 33;   // eta
+  fPDGToColor[4122]  = 34;   // Lambda_C+
+  fPDGToColor[3122]  = 35;   //  Lambda
+  fPDGToColor[24]    = 36;   // W+
+  fPDGToColor[3222]  = 37;   // Sigma+
+  fPDGToColor[-24]   = 38;   //  W-
+  fPDGToColor[3212]  = 39;   // Sigma0
+  fPDGToColor[23]    = 40;   //  Z
+  fPDGToColor[3112]  = 41;   // Sigma -
+  fPDGToColor[3322]  = 42;   // Xi0
+  fPDGToColor[3312]  = 43;   // Xi-
+  fPDGToColor[3334]  = 44;   // Omega- (PB)
+
+  fPDGToColor[50000050] = 801;  // Cerenkov
+
+  fPDGToColor[1000010020] = 45;  // PAL 31/05/2023: ???
+  fPDGToColor[1000010030] = 48;  // PAL 31/05/2023: ???
+  fPDGToColor[1000020040] = 50;  // PAL 31/05/2023: ???
+  fPDGToColor[1000020030] = 55;  // PAL 31/05/2023: ???
+}
+
+void CbmTimesliceManager::SetTransparencyForLayer(TGeoNode* node, Int_t depth, Char_t transparency)
+{
+  node->GetVolume()->SetTransparency(transparency);
+  if (depth <= 0) {  //
+    return;
+  }
+  for (int i = 0; i < node->GetNdaughters(); i++) {
+    TGeoNode* dau = node->GetDaughter(i);
+    SetTransparencyForLayer(dau, depth - 1, transparency);
+  }
+}
+
+bool CbmTimesliceManager::InitializeMainView(Int_t visopt, Int_t vislvl, Int_t maxvisnds)
+{
+  if (gGeoManager == nullptr) {  //
+    return false;
+  }
+  TGeoNode* N          = gGeoManager->GetTopNode();
+  TEveGeoTopNode* TNod = new TEveGeoTopNode(gGeoManager, N, visopt, vislvl, maxvisnds);
+
+  if (!fXMLConfig.EqualTo("")) {  //
+    LoadXMLSettings();
+  }
+
+  gEve->AddGlobalElement(TNod);
+
+  gEve->FullRedraw3D(kTRUE);
+  fEvent = gEve->AddEvent(this);
+
+  fTimesliceNumberText = new TGLAnnotation(gEve->GetDefaultGLViewer(), "TS Number: ", 0.01, 0.94);
+  fTimesliceNumberText->SetTextSize(0.03);  // % of window diagonal
+  fTimesliceNumberText->SetTextColor(kOrange - 2);
+
+  fTimesliceTimeText = new TGLAnnotation(gEve->GetDefaultGLViewer(), "TS Time: ", 0.01, 0.90);
+  fTimesliceTimeText->SetTextSize(0.03);  // % of window diagonal
+  fTimesliceTimeText->SetTextColor(kOrange - 2);
+
+  fEventNumberText = new TGLAnnotation(gEve->GetDefaultGLViewer(), "Event Number: ", 0.01, 0.86);
+  fEventNumberText->SetTextSize(0.03);  // % of window diagonal
+  fEventNumberText->SetTextColor(kOrange - 2);
+
+  fEventTimeText = new TGLAnnotation(gEve->GetDefaultGLViewer(), "Event Time: ", 0.01, 0.82);
+  fEventTimeText->SetTextSize(0.03);  // % of window diagonal
+  fEventTimeText->SetTextColor(kOrange - 2);
+
+  return true;
+}
+
+void CbmTimesliceManager::InitializeViewsCbm()
+{
+  fRPhiProjManager = new TEveProjectionManager(TEveProjection::kPT_RPhi);
+  fRhoZProjManager = new TEveProjectionManager(TEveProjection::kPT_RhoZ);
+  gEve->AddToListTree(fRPhiProjManager, kFALSE);
+  gEve->AddToListTree(fRhoZProjManager, kFALSE);
+  fAxesPhi = new TEveProjectionAxes(fRPhiProjManager);
+  fAxesRho = new TEveProjectionAxes(fRhoZProjManager);
+
+  fRPhiView  = gEve->SpawnNewViewer("RPhi View", "");
+  fRPhiScene = gEve->SpawnNewScene("RPhi", "Scene holding axis.");
+  fRPhiScene->AddElement(fAxesPhi);
+
+  fRhoZView  = gEve->SpawnNewViewer("RhoZ View", "");
+  fRhoZScene = gEve->SpawnNewScene("RhoZ", "Scene holding axis.");
+  fRhoZScene->AddElement(fAxesRho);
+
+  SetViewers(fRPhiView, fRhoZView);
+
+  TEveWindowSlot* MultiSlot = TEveWindow::CreateWindowInTab(gEve->GetBrowser()->GetTabRight());
+  TEveWindowPack* MultiPack = MultiSlot->MakePack();
+  MultiPack->SetElementName("Multi View");
+  MultiPack->SetHorizontal();
+  MultiPack->SetShowTitleBar(kFALSE);
+  MultiPack->NewSlot()->MakeCurrent();
+  fMultiView = gEve->SpawnNewViewer("3D View (multi)", "");
+  // switch off left and right light sources for 3D MultiView
+  // TODO: investigate and tune which light source directions should be used for best visibility
+  fMultiView->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightLeft, false);
+  fMultiView->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightRight, false);
+  // add 3D scenes (first tab) to 3D MultiView
+  fMultiView->AddScene(gEve->GetGlobalScene());
+  fMultiView->AddScene(gEve->GetEventScene());
+
+  // add slot for RPhi projection on Multi View tab
+  MultiPack = MultiPack->NewSlot()->MakePack();
+  MultiPack->SetShowTitleBar(kFALSE);
+  MultiPack->NewSlot()->MakeCurrent();
+  fMultiRPhiView = gEve->SpawnNewViewer("RPhi View (multi)", "");
+  MultiPack->NewSlot()->MakeCurrent();
+  fMultiRhoZView = gEve->SpawnNewViewer("RhoZ View (multi)", "");
+
+  SetViewers(fMultiRPhiView, fMultiRhoZView);
+
+  // don't change reposition camera on each update
+  fRPhiView->GetGLViewer()->SetResetCamerasOnUpdate(kFALSE);
+  fRhoZView->GetGLViewer()->SetResetCamerasOnUpdate(kFALSE);
+  fMultiView->GetGLViewer()->SetResetCamerasOnUpdate(kFALSE);
+  fMultiRPhiView->GetGLViewer()->SetResetCamerasOnUpdate(kFALSE);
+  fMultiRhoZView->GetGLViewer()->SetResetCamerasOnUpdate(kFALSE);
+  fMultiView->GetEveFrame()->HideAllDecorations();
+  fMultiRPhiView->GetEveFrame()->HideAllDecorations();
+  fMultiRhoZView->GetEveFrame()->HideAllDecorations();
+}
+
+void CbmTimesliceManager::SetViewers(TEveViewer* RPhi, TEveViewer* RhoZ)
+{
+  RPhi->GetGLViewer()->SetCurrentCamera(fRphiCam);
+  // set camera parameters
+  RPhi->GetGLViewer()->GetCameraOverlay()->SetOrthographicMode(TGLCameraOverlay::kAxis);
+  RPhi->GetGLViewer()->GetCameraOverlay()->SetShowOrthographic(kTRUE);
+  // switch off left, right, top and bottom light sources
+  // TODO: investigate and tune which light source directions should be used for best visibility
+  RPhi->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightLeft, false);
+  RPhi->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightRight, false);
+  RPhi->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightTop, false);
+  RPhi->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightBottom, false);
+
+  RhoZ->GetGLViewer()->SetCurrentCamera(fRhoCam);
+  // set camera parameters
+  RhoZ->GetGLViewer()->GetCameraOverlay()->SetOrthographicMode(TGLCameraOverlay::kAxis);
+  RhoZ->GetGLViewer()->GetCameraOverlay()->SetShowOrthographic(kTRUE);
+  // switch off left, right and front light sources
+  // TODO: investigate and tune which light source directions should be used for best visibility
+  RhoZ->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightLeft, false);
+  RhoZ->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightRight, false);
+  RhoZ->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightFront, false);
+
+  RPhi->AddScene(fRPhiScene);
+  RPhi->AddScene(gEve->GetGlobalScene());
+  RPhi->AddScene(gEve->GetEventScene());
+  RhoZ->AddScene(fRhoZScene);
+  RhoZ->AddScene(gEve->GetGlobalScene());
+  RhoZ->AddScene(gEve->GetEventScene());
+}
+
+void CbmTimesliceManager::InitializeViewsMcbm()
+{
+  // FIXME: available only starting from Fairsoft apr22 (did not check in which ROOT version it was introduced)
+  //fProjManagerZY = new TEveProjectionManager(TEveProjection::kPT_ZY);
+  fProjManagerZY = new TEveProjectionManager(TEveProjection::kPT_RhoZ);
+  //fProjManagerZX = new TEveProjectionManager(TEveProjection::kPT_ZX);
+  fProjManagerZX = new TEveProjectionManager(TEveProjection::kPT_RhoZ);
+  gEve->AddToListTree(fProjManagerZY, kFALSE);
+  gEve->AddToListTree(fProjManagerZX, kFALSE);
+  fAxesZY = new TEveProjectionAxes(fProjManagerZY);
+  fAxesZX = new TEveProjectionAxes(fProjManagerZX);
+
+  fViewZY  = gEve->SpawnNewViewer("ZY View", "");
+  fSceneZY = gEve->SpawnNewScene("ZY", "Scene holding axis.");
+  fSceneZY->AddElement(fAxesZY);
+
+  fViewZX  = gEve->SpawnNewViewer("ZX View", "");
+  fSceneZX = gEve->SpawnNewScene("ZX", "Scene holding axis.");
+  fSceneZX->AddElement(fAxesZX);
+
+  SetMcbmViewers(fViewZY, fViewZX);
+
+  TEveWindowSlot* McbmSlot = TEveWindow::CreateWindowInTab(gEve->GetBrowser()->GetTabRight());
+  TEveWindowPack* McbmPack = McbmSlot->MakePack();
+  McbmPack->SetElementName("mCBM View");
+  McbmPack->SetHorizontal();
+  McbmPack->SetShowTitleBar(kFALSE);
+  McbmPack->NewSlot()->MakeCurrent();
+  fMcbmView = gEve->SpawnNewViewer("3D View (multi)", "");
+  // switch off left and right light sources for 3D MultiView
+  // TODO: investigate and tune which light source directions should be used for best visibility
+  //fMcbmView->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightLeft, false);
+  //fMcbmView->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightRight, false);
+  // add 3D scenes (first tab) to 3D MultiView
+  fMcbmView->AddScene(gEve->GetGlobalScene());
+  fMcbmView->AddScene(gEve->GetEventScene());
+  // Center the 3D MultiView
+  fMcbmView->GetGLViewer()->CurrentCamera().SetCenterVecWarp(0.0, 0.0, 150.0);
+
+  // add slot for XY projection on Multi View tab
+  McbmPack = McbmPack->NewSlot()->MakePack();
+  McbmPack->SetShowTitleBar(kFALSE);
+  McbmPack->NewSlot()->MakeCurrent();
+  fMcbmViewZY = gEve->SpawnNewViewer("ZY View (multi)", "");
+  McbmPack->NewSlot()->MakeCurrent();
+  fMcbmViewZX = gEve->SpawnNewViewer("ZX View (multi)", "");
+
+  SetMcbmViewers(fMcbmViewZY, fMcbmViewZX);
+
+  // don't change reposition camera on each update
+  fViewZY->GetGLViewer()->SetResetCamerasOnUpdate(kFALSE);
+  fViewZX->GetGLViewer()->SetResetCamerasOnUpdate(kFALSE);
+  fMcbmView->GetGLViewer()->SetResetCamerasOnUpdate(kFALSE);
+  fMcbmViewZY->GetGLViewer()->SetResetCamerasOnUpdate(kFALSE);
+  fMcbmViewZX->GetGLViewer()->SetResetCamerasOnUpdate(kFALSE);
+  fMcbmView->GetEveFrame()->HideAllDecorations();
+  fMcbmViewZY->GetEveFrame()->HideAllDecorations();
+  fMcbmViewZX->GetEveFrame()->HideAllDecorations();
+
+  // Set clear background
+  fViewZY->GetGLViewer()->SetClearColor(kYellow - 10);
+  fViewZX->GetGLViewer()->SetClearColor(kYellow - 10);
+  fMcbmView->GetGLViewer()->SetClearColor(kYellow - 10);
+  fMcbmViewZY->GetGLViewer()->SetClearColor(kYellow - 10);
+  fMcbmViewZX->GetGLViewer()->SetClearColor(kYellow - 10);
+}
+
+void CbmTimesliceManager::SetMcbmViewers(TEveViewer* ZY, TEveViewer* ZX)
+{
+  ZY->GetGLViewer()->SetCurrentCamera(fCamZY);
+  // set camera parameters
+  ZY->GetGLViewer()->GetCameraOverlay()->SetOrthographicMode(TGLCameraOverlay::kAxis);
+  ZY->GetGLViewer()->GetCameraOverlay()->SetShowOrthographic(kTRUE);
+  // switch off left, right and front light sources
+  // TODO: investigate and tune which light source directions should be used for best visibility
+  ZY->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightLeft, false);
+  //ZY->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightRight, false);
+  ZY->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightFront, false);
+  // Set Camera Center
+  ZY->GetGLViewer()->CurrentCamera().SetCenterVecWarp(0.0, 0.0, 150.0);
+
+  ZY->AddScene(fSceneZY);
+  ZY->AddScene(gEve->GetGlobalScene());
+  ZY->AddScene(gEve->GetEventScene());
+
+  ZX->GetGLViewer()->SetCurrentCamera(fCamZX);
+  // set camera parameters
+  ZX->GetGLViewer()->GetCameraOverlay()->SetOrthographicMode(TGLCameraOverlay::kAxis);
+  ZX->GetGLViewer()->GetCameraOverlay()->SetShowOrthographic(kTRUE);
+  // switch off left, right, top and bottom light sources
+  // TODO: investigate and tune which light source directions should be used for best visibility
+  ZX->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightLeft, false);
+  //ZX->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightRight, false);
+  ZX->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightTop, false);
+  ZX->GetGLViewer()->GetLightSet()->SetLight(TGLLightSet::kLightBottom, false);
+  // Set Camera Center
+  ZX->GetGLViewer()->CurrentCamera().SetCenterVecWarp(0.0, 0.0, 150.0);
+
+  ZX->AddScene(fSceneZX);
+  ZX->AddScene(gEve->GetGlobalScene());
+  ZX->AddScene(gEve->GetEventScene());
+}
+// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //
+
+ClassImp(CbmTimesliceManager)
diff --git a/core/eventdisplay/CbmTimesliceManager.h b/core/eventdisplay/CbmTimesliceManager.h
new file mode 100644
index 0000000000..6c575cf6db
--- /dev/null
+++ b/core/eventdisplay/CbmTimesliceManager.h
@@ -0,0 +1,171 @@
+/* Copyright (C) 2023 Facility for Antiproton and Ion Research in Europe, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau[committer] */
+
+/// PAL 31/05/2023: based on FairEventManager from FairRoot v18.6.7
+
+#ifndef CbmTimesliceManager_H
+#define CbmTimesliceManager_H
+
+#include "CbmTsEveAnimationControl.h"
+
+#include "FairRunAna.h"  // for FairRunAna
+
+#include <Rtypes.h>            // for Float_t, Int_t, Bool_t, etc
+#include <TEveEventManager.h>  // for TEveEventManager
+#include <TEveProjectionAxes.h>
+#include <TGLViewer.h>
+
+#include <map>
+
+class FairRootManager;
+class FairRunAna;
+class FairTask;
+class FairXMLNode;
+
+class TClonesArray;
+class TEveProjectionAxes;
+class TEveProjectionManager;
+class TEveScene;
+class TEveText;
+class TEveViewer;
+class TGeoNode;
+class TGLAnnotation;
+class TGListTreeItem;
+
+class CbmTimesliceManager : public TEveEventManager {
+public:
+  static CbmTimesliceManager* Instance();
+  CbmTimesliceManager();
+  virtual ~CbmTimesliceManager() = default;
+
+  CbmTimesliceManager(const CbmTimesliceManager&) = delete;
+  CbmTimesliceManager& operator=(const CbmTimesliceManager&) = delete;
+
+  virtual void SetXMLConfig(TString xml_config) { fXMLConfig = xml_config; }
+  void SetDisplayCbmElectron();
+  void SetDisplayCbmMuon();
+  void SetDisplayMcbm();
+  /**
+   * set detector's transparency
+   * @param use_xml use xml colors if available
+   * @param trans transparency for detector (if xml not used)
+   */
+  virtual void SetTransparency(Bool_t use_xml, Int_t trans);
+  /**
+   * switch background color
+   * @param light use white if true
+   */
+  virtual void SwitchBackground(Bool_t /*light*/);
+
+  virtual void Init(Int_t visopt = 1, Int_t vislvl = 3, Int_t maxvisnds = 10000);
+  void AddTask(FairTask* t) { fRunAna->AddTask(t); }
+
+  virtual void Open();
+  void UpdateEditor();
+  virtual void Close();
+  virtual Int_t Color(Int_t pdg);
+  virtual void DisplaySettings();                  // *MENU*
+  virtual void GotoTimeslice(uint32_t timeslice);  // *MENU*
+  virtual void NextTimeslice();                    // *MENU*
+  virtual void PrevTimeslice();                    // *MENU*
+  virtual void GotoEvent(uint32_t event);          // *MENU*
+  virtual void NextEvent();                        // *MENU*
+  virtual void PrevEvent();                        // *MENU*
+
+  bool GetMcbmViewersMode() const { return fbMcbmViewersEna; }
+  virtual uint32_t GetCurrentTimeslice() const { return fTimesliceIdx; }
+  virtual Int_t GetCurrentEvent() const { return fEventIdx; }
+  double_t GetTimesliceTime();  ///< current time in ns to display in the event display.
+  double_t GetEventTime();      ///< current time in ns to display in the event display.
+
+  virtual void SetTsTimeText(Double_t time);
+  virtual void SetTsNumberText(Int_t tsNumber);
+  virtual void SetEvtTimeText(Double_t time);
+  virtual void SetEvtNumberText(Int_t evtNumber);
+
+  void SetClearHandler(Bool_t val) { fClearHandler = val; }  ///< Used to indicate to subtask that they should reset
+  Bool_t GetClearHandler() const { return fClearHandler; }   ///< Used to indicate to subtask that they should reset
+
+  /**
+   *
+   * @param name name of file with screenshot
+   * @param proj 0 - 3D view, 1 - RPhi, 2 RhoZ, 3 - all
+   * @param def_path - default path to screenshot, if empty -user will be asked
+   */
+  void MakeScreenshot(CbmTsEveAnimationControl::eScreenshotType screenshotType, TString def_path = "");
+
+protected:
+  virtual void LoadXMLSettings();
+  void LoadXMLDetector(TGeoNode* node, FairXMLNode* xml, Int_t depth = 0);
+  Int_t StringToColor(TString color) const;
+
+private:
+  static CbmTimesliceManager* gRinstanceTsMan;  //!
+
+  FairRunAna* fRunAna           = nullptr;  //!
+  FairRootManager* fRootManager = nullptr;  //!
+
+  TString fXMLConfig             = "";
+  std::map<int, int> fPDGToColor = {};
+  void AddParticlesToPdgDataBase();
+  void InitPdgColorMap();
+  void SetTransparencyForLayer(TGeoNode* node, Int_t depth, Char_t transparency);
+
+  Bool_t fClearHandler = kFALSE;  //!
+
+  TGListTreeItem* fEvent   = nullptr;  //!
+  Int_t fEntry             = 0;        //!
+  TClonesArray* fCbmEvents = nullptr;  //!
+  uint32_t fTimesliceIdx   = 0;        //!
+  uint32_t fEventIdx       = 0;        //!
+  double_t fTimeTimeslice  = -1;       //!
+  double_t fTimeEvent      = -1;       //!
+
+  TGLAnnotation* fTimesliceTimeText   = nullptr;  //!
+  TGLAnnotation* fTimesliceNumberText = nullptr;  //!
+  TGLAnnotation* fEventTimeText       = nullptr;  //!
+  TGLAnnotation* fEventNumberText     = nullptr;  //!
+
+  TGLViewer::ECameraType fRphiCam = TGLViewer::kCameraOrthoXOY;   //!
+  TGLViewer::ECameraType fRhoCam  = TGLViewer::kCameraOrthoZOY;   //!
+  TGLViewer::ECameraType fCamZY   = TGLViewer::kCameraOrthoZnOX;  //!
+  TGLViewer::ECameraType fCamZX   = TGLViewer::kCameraOrthoZOY;   //!
+
+  /// CBM views
+  TEveViewer* fRPhiView                   = nullptr;  //!
+  TEveViewer* fRhoZView                   = nullptr;  //!
+  TEveViewer* fMultiView                  = nullptr;  //!
+  TEveViewer* fMultiRPhiView              = nullptr;  //!
+  TEveViewer* fMultiRhoZView              = nullptr;  //!
+  TEveScene* fRPhiScene                   = nullptr;  //!
+  TEveScene* fRhoZScene                   = nullptr;  //!
+  TEveProjectionManager* fRPhiProjManager = nullptr;  //!
+  TEveProjectionManager* fRhoZProjManager = nullptr;  //!
+  TEveProjectionAxes* fAxesPhi            = nullptr;  //!
+  TEveProjectionAxes* fAxesRho            = nullptr;  //!
+
+  /// mCBM views
+  bool fbMcbmViewersEna                 = false;    //!
+  TEveViewer* fViewZY                   = nullptr;  //!
+  TEveViewer* fViewZX                   = nullptr;  //!
+  TEveViewer* fMcbmView                 = nullptr;  //!
+  TEveViewer* fMcbmViewZY               = nullptr;  //!
+  TEveViewer* fMcbmViewZX               = nullptr;  //!
+  TEveScene* fSceneZY                   = nullptr;  //!
+  TEveScene* fSceneZX                   = nullptr;  //!
+  TEveProjectionManager* fProjManagerZY = nullptr;  //!
+  TEveProjectionManager* fProjManagerZX = nullptr;  //!
+  TEveProjectionAxes* fAxesZY           = nullptr;  //!
+  TEveProjectionAxes* fAxesZX           = nullptr;  //!
+
+  bool InitializeMainView(Int_t visopt, Int_t vislvl, Int_t maxvisnds);
+  void InitializeViewsCbm();
+  void SetViewers(TEveViewer* RPhi, TEveViewer* RhoZ);
+  void InitializeViewsMcbm();
+  void SetMcbmViewers(TEveViewer* ZY, TEveViewer* ZX);
+
+  ClassDef(CbmTimesliceManager, 1);
+};
+
+#endif  // CbmTimesliceManager_H
diff --git a/core/eventdisplay/CbmTimesliceManagerEditor.cxx b/core/eventdisplay/CbmTimesliceManagerEditor.cxx
new file mode 100644
index 0000000000..95d9aed56c
--- /dev/null
+++ b/core/eventdisplay/CbmTimesliceManagerEditor.cxx
@@ -0,0 +1,464 @@
+/* Copyright (C) 2023 Facility for Antiproton and Ion Research in Europe, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau[committer] */
+/// Based on CbmTimesliceManagerEditor class of FairRoot v18.6.7
+
+#include "CbmTimesliceManagerEditor.h"
+
+#include "CbmEvent.h"                     // For CbmEvent
+#include "CbmTimesliceManager.h"          // for CbmTimesliceManager
+#include "CbmTsEveAnimationControl.h"     // for CbmTsEveAnimationControl
+#include "CbmTsEveTransparencyControl.h"  // for CbmTsEveTransparencyControl
+
+#include "FairRootManager.h"  // for FairRootManager
+#include "FairRunAna.h"       // for FairRunAna
+#include "FairTask.h"         // for FairTask
+
+#include <RtypesCore.h>         // for Double_t, Int_t, UInt_t, Bool_t
+#include <TChain.h>             // for TChain
+#include <TClonesArray.h>       // for TClonesArray
+#include <TEveManager.h>        // for TEveManager, gEve
+#include <TFile.h>              // for TFile
+#include <TGButton.h>           // for TGTextButton, TGCheckButton
+#include <TGComboBox.h>         // for TGComboBox
+#include <TGLabel.h>            // for TGLabel
+#include <TGLayout.h>           // for TGLayoutHints, kLHintsExpandX
+#include <TGNumberEntry.h>      // for TGNumberEntry, TGNumberFormat
+#include <TGenericClassInfo.h>  // for TGenericClassInfo
+#include <TGeoManager.h>        // for TGeoManager, gGeoManager
+#include <TList.h>              // for TObjLink, TList
+#include <TString.h>            // for TString, Form
+#include <TSystem.h>            // for TSystem, gSystem
+
+#include <chrono>
+#include <iostream>
+#include <memory>  // for unique_ptr
+#include <thread>
+
+class TGWindow;  // lines 36-36
+class TObject;   // lines 37-37
+
+#define MAXE 5000
+
+// CbmTimesliceManagerEditor
+//
+// Specialization of TGedEditor for proper update propagation to TEveManager.
+
+CbmTimesliceManagerEditor::CbmTimesliceManagerEditor(const TGWindow* p, Int_t width, Int_t height, UInt_t options,
+                                                     Pixel_t back)
+  : TGedFrame(p, width, height, options | kVerticalFrame, back)
+  , fObject(0)
+  , fManager(CbmTimesliceManager::Instance())
+  , fCurrentEvent(0)
+  , fGlobalTransparency(nullptr)
+  , fEventTime(nullptr)
+  , fScreenshotOpt(nullptr)
+{
+  Init();
+}
+
+void CbmTimesliceManagerEditor::SwitchBackground(Bool_t light_background)
+{
+  fManager->SwitchBackground(light_background);
+}
+
+void CbmTimesliceManagerEditor::Init()
+{
+  FairRootManager* rootManager = FairRootManager::Instance();
+  TChain* chain                = rootManager->GetInChain();
+  fNbTs                        = chain->GetEntriesFast();
+
+  MakeTitle("CbmTimesliceManager Editor");
+  TGVerticalFrame* fInfoFrame = CreateEditorTabSubFrame("Info TS");
+  TGCompositeFrame* title1 =
+    new TGCompositeFrame(fInfoFrame, fWidth, 10, kVerticalFrame | kLHintsExpandX | kFixedWidth | kOwnBackground);
+
+  TString Infile = "Input file : ";
+  TFile* file    = FairRootManager::Instance()->GetInChain()->GetFile();
+  Infile += file->GetName();
+  TGLabel* TFName = new TGLabel(title1, Infile.Data());
+  title1->AddFrame(TFName);
+
+  Int_t nodes    = gGeoManager->GetNNodes();
+  TString NNodes = "No. of Nodes : ";
+  NNodes += nodes;
+  TGLabel* NoNode = new TGLabel(title1, NNodes.Data());
+  title1->AddFrame(NoNode);
+
+  UInt_t RunId = FairRunAna::Instance()->getRunId();
+  TString run  = "Run Id : ";
+  run += RunId;
+  TGLabel* TRunId = new TGLabel(title1, run.Data());
+  title1->AddFrame(TRunId);
+
+  TString ntimeslices = "No of timeslices : ";
+  ntimeslices += fNbTs;
+  TGLabel* TNbTimeslices = new TGLabel(title1, ntimeslices.Data());
+  title1->AddFrame(TNbTimeslices);
+
+  TGHorizontalFrame* fTsIdx = new TGHorizontalFrame(title1);
+  TGLabel* lTsIdx           = new TGLabel(fTsIdx, "Current TS:");
+  fTsIdx->AddFrame(lTsIdx, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 1, 2, 1, 1));
+  fCurrentTimeslice = new TGNumberEntry(fTsIdx, 0., 6, -1, TGNumberFormat::kNESInteger, TGNumberFormat::kNEANonNegative,
+                                        TGNumberFormat::kNELLimitMinMax, 0, fNbTs);
+  fTsIdx->AddFrame(fCurrentTimeslice, new TGLayoutHints(kLHintsLeft, 1, 1, 1, 1));
+  fCurrentTimeslice->Connect("ValueSet(Long_t)", "CbmTimesliceManagerEditor", this, "SelectSingleTimeslice()");
+  title1->AddFrame(fTsIdx);
+
+  TGHorizontalFrame* fTsTime = new TGHorizontalFrame(title1);
+  TGLabel* TsTimeLabel       = new TGLabel(fTsTime, "TS Time: ");
+  fTimesliceTime             = new TGLabel(fTsTime, "");
+  fTsTime->AddFrame(TsTimeLabel);
+  fTsTime->AddFrame(fTimesliceTime);
+  title1->AddFrame(fTsTime);
+
+  TString nevent = "No of events in TS : ";
+  nevent += (Int_t)(-1);
+  fEventNb = new TGLabel(title1, nevent.Data());
+  title1->AddFrame(fEventNb);
+
+  fSelTs = new TGTextButton(title1, "Select TS");
+  fSelTs->Connect("Clicked()", "CbmTimesliceManagerEditor", this, "SelectSingleTimeslice()");
+  title1->AddFrame(fSelTs, new TGLayoutHints(kLHintsRight | kLHintsExpandX, 5, 5, 1, 1));
+
+  fPrevTs = new TGTextButton(title1, "Prev TS");
+  fPrevTs->Connect("Clicked()", "CbmTimesliceManagerEditor", this, "PrevTimeslice()");
+  title1->AddFrame(fPrevTs, new TGLayoutHints(kLHintsRight | kLHintsExpandX, 5, 5, 1, 1));
+
+  fNextTs = new TGTextButton(title1, "Next TS");
+  fNextTs->Connect("Clicked()", "CbmTimesliceManagerEditor", this, "NextTimeslice()");
+  title1->AddFrame(fNextTs, new TGLayoutHints(kLHintsRight | kLHintsExpandX, 5, 5, 1, 1));
+
+
+  TGHorizontalFrame* fEvtIdx = new TGHorizontalFrame(title1);
+  TGLabel* lEvtIdx           = new TGLabel(fEvtIdx, "Current Event:");
+  fEvtIdx->AddFrame(lEvtIdx, new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 1, 2, 1, 1));
+  fCurrentEvent = new TGNumberEntry(fEvtIdx, 0., 6, -1, TGNumberFormat::kNESInteger, TGNumberFormat::kNEANonNegative,
+                                    TGNumberFormat::kNELLimitMinMax, 0, -1);
+  fEvtIdx->AddFrame(fCurrentEvent, new TGLayoutHints(kLHintsLeft, 1, 1, 1, 1));
+  fCurrentTimeslice->Connect("ValueSet(Long_t)", "CbmTimesliceManagerEditor", this, "SelectSingleTimeslice()");
+  title1->AddFrame(fEvtIdx);
+
+
+  TGHorizontalFrame* fEvtTime = new TGHorizontalFrame(title1);
+  TGLabel* EventTimeLabel     = new TGLabel(fEvtTime, "Event Time: ");
+  fEventTime                  = new TGLabel(fEvtTime, "");
+  fEvtTime->AddFrame(EventTimeLabel);
+  fEvtTime->AddFrame(fEventTime);
+  title1->AddFrame(fEvtTime);
+
+  fUpdateEvent = new TGTextButton(title1, "Update Event");
+  fUpdateEvent->Connect("Clicked()", "CbmTimesliceManagerEditor", this, "SelectSingleEvent()");
+  title1->AddFrame(fUpdateEvent, new TGLayoutHints(kLHintsRight | kLHintsExpandX, 5, 5, 1, 1));
+
+  fPrevEvent = new TGTextButton(title1, "Prev Event");
+  fPrevEvent->Connect("Clicked()", "CbmTimesliceManagerEditor", this, "PrevEvent()");
+  title1->AddFrame(fPrevEvent, new TGLayoutHints(kLHintsRight | kLHintsExpandX, 5, 5, 1, 1));
+
+  fNextEvent = new TGTextButton(title1, "Next Event");
+  fNextEvent->Connect("Clicked()", "CbmTimesliceManagerEditor", this, "NextEvent()");
+  title1->AddFrame(fNextEvent, new TGLayoutHints(kLHintsRight | kLHintsExpandX, 5, 5, 1, 1));
+
+  /// Disable all Prev/Next TS buttons until first TS loaded/selected!
+  fPrevTs->SetEnabled(kFALSE);
+  fNextTs->SetEnabled(kFALSE);
+
+  /// Disable all Events buttons until first TS loaded/selected!
+  fUpdateEvent->SetEnabled(kFALSE);
+  fPrevEvent->SetEnabled(kFALSE);
+  fNextEvent->SetEnabled(kFALSE);
+
+
+  fInfoFrame->AddFrame(title1, new TGLayoutHints(kLHintsTop, 0, 0, 2, 0));
+
+  //=============== graphics =============================
+  TGVerticalFrame* scene_conf = CreateEditorTabSubFrame("Graphics");
+  // TGHorizontalFrame* transparency_frame = new TGHorizontalFrame(scene_conf); /// PAL: Unused in FairRoot original?!?
+
+  std::unique_ptr<CbmTsEveTransparencyControl> transparency(
+    new CbmTsEveTransparencyControl(scene_conf, "Global transparency"));
+  scene_conf->AddFrame(transparency.release(), new TGLayoutHints(kLHintsNormal, 5, 5, 1, 1));
+
+  TGCheckButton* backgroundButton = new TGCheckButton(scene_conf, "Light background");
+  scene_conf->AddFrame(backgroundButton, new TGLayoutHints(kLHintsRight | kLHintsExpandX, 5, 5, 1, 1));
+  backgroundButton->Connect("Toggled(Bool_t)", this->ClassName(), this, "SwitchBackground(Bool_t)");
+
+  TGGroupFrame* frame_screenshot = new TGGroupFrame(scene_conf, "Screenshot");
+  frame_screenshot->SetTitlePos(TGGroupFrame::kCenter);
+
+  frame_screenshot->SetLayoutManager(new TGHorizontalLayout(frame_screenshot));
+
+  TGTextButton* Screen = new TGTextButton(frame_screenshot, "Make");
+  frame_screenshot->AddFrame(Screen, new TGLayoutHints(kLHintsLeft | kLHintsExpandX, 20, 2, 2, 2));
+  Screen->Connect("Clicked()", this->ClassName(), this, "MakeScreenshot()");
+
+  fScreenshotOpt = new TGComboBox(frame_screenshot);
+  fScreenshotOpt->AddEntry("3D", CbmTsEveAnimationControl::eScreenshotType::k3D);
+  if (CbmTimesliceManager::Instance()->GetMcbmViewersMode()) {
+    fScreenshotOpt->AddEntry("ZX", CbmTsEveAnimationControl::eScreenshotType::kZX);
+    fScreenshotOpt->AddEntry("ZY", CbmTsEveAnimationControl::eScreenshotType::kZY);
+  }
+  else {
+    fScreenshotOpt->AddEntry("RPhi", CbmTsEveAnimationControl::eScreenshotType::kXY);
+    fScreenshotOpt->AddEntry("RhoZ", CbmTsEveAnimationControl::eScreenshotType::kZ);
+  }
+  fScreenshotOpt->AddEntry("All", CbmTsEveAnimationControl::eScreenshotType::kAll);
+  fScreenshotOpt->Select(0);
+  fScreenshotOpt->Resize(40, 30);
+  frame_screenshot->AddFrame(fScreenshotOpt, new TGLayoutHints(kLHintsRight | kLHintsExpandX));
+  scene_conf->AddFrame(frame_screenshot, new TGLayoutHints(kLHintsTop | kLHintsExpandX, 1, 1, 2, 1));
+
+  fAnimation = new CbmTsEveAnimationControl(this, scene_conf, "StartAnimation()", "Animation", fWidth);
+  fAnimation->SetTsNb(fNbTs);
+  fAnimation->SetDisplayMcbm(CbmTimesliceManager::Instance()->GetMcbmViewersMode());
+  fAnimation->Init();
+
+  // TODO: fine-tune the position of the elements in the GUI tab
+  //  scene_conf->AddFrame(online_screenshot,new TGLayoutHints(kLHintsRight|kLHintsExpandX));
+
+
+  fCbmEvents = dynamic_cast<TClonesArray*>(rootManager->GetObject("CbmEvent"));
+
+  if (nullptr == fCbmEvents) {
+    LOG(fatal) << "CbmTimesliceManager::Init() => CbmEvents branch not found! Task will be deactivated";
+  }
+}
+
+void CbmTimesliceManagerEditor::SelectSingleTimeslice()
+{
+  fManager->SetClearHandler(kTRUE);
+  SelectTimeslice();
+}
+
+void CbmTimesliceManagerEditor::SelectTimeslice()
+{
+  fManager->GotoTimeslice(fCurrentTimeslice->GetIntNumber());
+  SetTimesliceTimeLabel(CbmTimesliceManager::Instance()->GetTimesliceTime());
+  CbmTimesliceManager::Instance()->SetTsTimeText(CbmTimesliceManager::Instance()->GetTimesliceTime());
+  CbmTimesliceManager::Instance()->SetTsNumberText(fCurrentTimeslice->GetIntNumber());
+
+  /// Enable/Disable buttons to avoid invalid accesses
+  fPrevTs->SetEnabled((0 < fCurrentTimeslice->GetIntNumber()) ? kTRUE : kFALSE);
+  fNextTs->SetEnabled((fCurrentTimeslice->GetIntNumber() < (fNbTs - 1)) ? kTRUE : kFALSE);
+
+  fNbEventsInTs = fCbmEvents->GetEntriesFast();
+
+  TString nevent = "No of events in TS : ";
+  nevent += fNbEventsInTs;
+  fEventNb->ChangeText(nevent.Data());
+  fCurrentEvent->SetLimitValues(0, fNbEventsInTs);
+  fCurrentEvent->SetIntNumber(0);
+  fAnimation->SetEvtNb(fNbEventsInTs);
+
+  /// Enable/Disable buttons to avoid invalid accesses
+  if (0 < fNbEventsInTs) {
+    fUpdateEvent->SetEnabled(kTRUE);
+    fPrevEvent->SetEnabled(kFALSE);
+    if (1 < fNbEventsInTs) {  //
+      fNextEvent->SetEnabled(kTRUE);
+    }
+  }
+  else {
+    fUpdateEvent->SetEnabled(kFALSE);
+    fPrevEvent->SetEnabled(kFALSE);
+    fNextEvent->SetEnabled(kFALSE);
+  }
+
+  /// Load first event of this TS if possible and Update info text
+  if (0 < fNbEventsInTs) {  //
+    SelectEvent();
+  }
+}
+
+void CbmTimesliceManagerEditor::PrevTimeslice()
+{
+  LOG(debug1) << "CbmTimesliceManager::PrevTimeslice() => At timeslice " << fCurrentTimeslice->GetIntNumber();
+  uint32_t uTsIdx = fCurrentTimeslice->GetIntNumber();
+  if (0 < uTsIdx) {  // Should be protected by button enabling/disabling, but better safe than sorry
+    uTsIdx -= 1;
+    fCurrentTimeslice->SetIntNumber(uTsIdx);
+    LOG(debug1) << "CbmTimesliceManager::PrevTimeslice() => Setting timeslice to " << uTsIdx << " result "
+                << fCurrentTimeslice->GetIntNumber();
+
+    SelectSingleTimeslice();
+  }
+}
+
+void CbmTimesliceManagerEditor::NextTimeslice()
+{
+  LOG(debug1) << "CbmTimesliceManager::NextTimeslice() => At timeslice " << fCurrentTimeslice->GetIntNumber();
+  uint32_t uTsIdx = fCurrentTimeslice->GetIntNumber();
+  if (uTsIdx < fNbTs - 1) {  // Should be protected by button enabling/disabling, but better safe than sorry
+    uTsIdx += 1;
+    fCurrentTimeslice->SetIntNumber(uTsIdx);
+    LOG(debug1) << "CbmTimesliceManager::NextTimeslice() => Setting timeslice to " << uTsIdx << " result "
+                << fCurrentTimeslice->GetIntNumber();
+
+    SelectSingleTimeslice();
+  }
+}
+
+void CbmTimesliceManagerEditor::SelectSingleEvent()
+{
+  fManager->SetClearHandler(kTRUE);
+  SelectEvent();
+}
+
+void CbmTimesliceManagerEditor::SelectEvent()
+{
+  LOG(debug1) << "CbmTimesliceManager::SelectEvent() => Going to event " << fCurrentEvent->GetIntNumber();
+  fManager->GotoEvent(fCurrentEvent->GetIntNumber());
+  SetEventTimeLabel(CbmTimesliceManager::Instance()->GetEventTime());
+  CbmTimesliceManager::Instance()->SetEvtTimeText(CbmTimesliceManager::Instance()->GetEventTime());
+  CbmTimesliceManager::Instance()->SetEvtNumberText(fCurrentEvent->GetIntNumber());
+
+  // Enable/Disable buttons to avoid invalid accesses
+  fPrevEvent->SetEnabled((0 < fCurrentEvent->GetIntNumber()) ? kTRUE : kFALSE);
+  fNextEvent->SetEnabled((fCurrentEvent->GetIntNumber() < (fNbEventsInTs - 1)) ? kTRUE : kFALSE);
+}
+
+void CbmTimesliceManagerEditor::PrevEvent()
+{
+  LOG(debug1) << "CbmTimesliceManager::PrevEvent() => At event " << fCurrentEvent->GetIntNumber();
+  uint32_t uEventIdx = fCurrentEvent->GetIntNumber();
+  if (0 < uEventIdx) {  // Should be protected by button enabling/disabling, but better safe than sorry
+    uEventIdx -= 1;
+    fCurrentEvent->SetIntNumber(uEventIdx);
+    LOG(debug1) << "CbmTimesliceManager::PrevEvent() => Setting event to " << uEventIdx << " result "
+                << fCurrentEvent->GetIntNumber();
+
+    SelectSingleEvent();
+  }
+}
+
+void CbmTimesliceManagerEditor::NextEvent()
+{
+  LOG(debug1) << "CbmTimesliceManager::NextEvent() => At event " << fCurrentEvent->GetIntNumber();
+  uint32_t uEventIdx = fCurrentEvent->GetIntNumber();
+  if (uEventIdx < fNbEventsInTs - 1) {  // Should be protected by button enabling/disabling, but better safe than sorry
+    uEventIdx += 1;
+    fCurrentEvent->SetIntNumber(uEventIdx);
+    LOG(debug1) << "CbmTimesliceManager::NextEvent() => Setting event to " << uEventIdx << " result "
+                << fCurrentEvent->GetIntNumber();
+
+    SelectSingleEvent();
+  }
+}
+
+void CbmTimesliceManagerEditor::SetTimesliceTimeLabel(Double_t time)
+{
+  TString stime;
+  stime.Form("%.2f", time);
+  stime += " ns";
+  fTimesliceTime->SetText(stime.Data());
+  Update();
+}
+void CbmTimesliceManagerEditor::SetEventTimeLabel(Double_t time)
+{
+  TString stime;
+  stime.Form("%.2f", time);
+  stime += " ns";
+  fEventTime->SetText(stime.Data());
+  Update();
+}
+
+void CbmTimesliceManagerEditor::SetModel(TObject* obj) { fObject = obj; }
+
+void CbmTimesliceManagerEditor::StartAnimation()
+{
+  CbmTsEveAnimationControl::eScreenshotType screen = fAnimation->GetScreenshotType();
+
+  std::chrono::duration<double> secSleepTime(fAnimation->GetAnimFrameSec());
+  bool bSleep = (0.0 < fAnimation->GetAnimFrameSec());
+
+  switch (fAnimation->GetAnimationType()) {
+    case CbmTsEveAnimationControl::eAnimationType::kEventsInTs: {  // Events in selected timeslice
+      /// Get minimum and maximum event indices, ensured to fit range in TS by buttons updates
+      uint32_t iEvtMin  = static_cast<uint32_t>(fAnimation->GetEventMin());
+      uint32_t iEvtMax  = static_cast<uint32_t>(fAnimation->GetEventMax());
+      uint32_t iEvtStep = static_cast<uint32_t>(fAnimation->GetEventStep());
+
+      if (fAnimation->GetScreenshotEna()) {  //
+        gSystem->mkdir("event_animations");
+      }
+      for (uint32_t iEvt = iEvtMin; iEvt < iEvtMax; iEvt += iEvtStep) {
+        fCurrentEvent->SetIntNumber(iEvt);
+
+        if ((iEvtMin == iEvt && kTRUE == fAnimation->GetClearBuffer()) || kFALSE == fAnimation->GetRunContinuous()) {
+          /// Clear display at startup if requested (should be default for most uses)
+          /// Clear display after each event if requested (should be default for most uses)
+          fManager->SetClearHandler(kTRUE);
+        }
+        else {
+          /// Do not clear display between events = accumulate/stack them
+          fManager->SetClearHandler(kFALSE);
+        }
+
+        SelectEvent();
+        gEve->FullRedraw3D();
+        if (fAnimation->GetScreenshotEna()) {  //
+          fManager->MakeScreenshot(screen, Form("event_animations/event_%05i.png", iEvt));
+        }
+        if (bSleep) {
+          // sleep between events to be able to see them
+          std::this_thread::sleep_for(secSleepTime);
+        }
+      }
+      break;
+    }
+    case CbmTsEveAnimationControl::eAnimationType::kTimeSlices: {  // Events in timeslice range
+      /// Get minimum and maximum TS indices, ensured to fit range in run by buttons updates
+      uint32_t iTsMin  = static_cast<uint32_t>(fAnimation->GetTsMin());
+      uint32_t iTsMax  = static_cast<uint32_t>(fAnimation->GetTsMax());
+      uint32_t iTsStep = static_cast<uint32_t>(fAnimation->GetTsStep());
+
+      /// Get only event step, min and max set to content of each TS
+      uint32_t iEvtStep = static_cast<int32_t>(fAnimation->GetEventStep());
+
+      if (fAnimation->GetScreenshotEna()) {  //
+        gSystem->mkdir("timeslice_animations");
+      }
+      for (uint32_t iTs = iTsMin; iTs < iTsMax; iTs += iTsStep) {
+        fCurrentTimeslice->SetIntNumber(iTs);
+
+        if (iTsMin == iTs && kTRUE == fAnimation->GetClearBuffer()) {
+          /// Clear display at startup if requested (should be default for most uses)
+          fManager->SetClearHandler(kTRUE);
+        }
+
+        SelectTimeslice();  /// <= This should update all events limits to appropriate values
+        for (uint32_t iEvt = 0; iEvt < fNbEventsInTs; iEvt += iEvtStep) {
+          fCurrentEvent->SetIntNumber(iEvt);
+
+          if (kFALSE == fAnimation->GetRunContinuous()) {
+            /// Clear display after each event if requested (should be default for most uses)
+            fManager->SetClearHandler(kTRUE);
+          }
+          else {
+            /// Do not clear display between events = accumulate/stack them
+            fManager->SetClearHandler(kFALSE);
+          }
+
+          SelectEvent();
+          gEve->FullRedraw3D();
+          if (fAnimation->GetScreenshotEna()) {  //
+            fManager->MakeScreenshot(screen, Form("timeslice_animations/ts_%05i_event_%05i.png", iTs, iEvt));
+          }
+          if (bSleep) {
+            // sleep between events to be able to see them
+            std::this_thread::sleep_for(secSleepTime);
+          }
+        }  // Event loop
+      }    // TS loop
+      break;
+    }
+  }
+  LOG(info) << "CbmTimesliceManager::StartAnimation() => Done with animation";
+}
+
+void CbmTimesliceManagerEditor::MakeScreenshot()
+{
+  fManager->MakeScreenshot(static_cast<CbmTsEveAnimationControl::eScreenshotType>(fScreenshotOpt->GetSelected()));
+}
+
+ClassImp(CbmTimesliceManagerEditor)
diff --git a/core/eventdisplay/CbmTimesliceManagerEditor.h b/core/eventdisplay/CbmTimesliceManagerEditor.h
new file mode 100644
index 0000000000..61098e9f21
--- /dev/null
+++ b/core/eventdisplay/CbmTimesliceManagerEditor.h
@@ -0,0 +1,92 @@
+/* Copyright (C) 2023 Facility for Antiproton and Ion Research in Europe, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau[committer] */
+/// Based on CbmTimesliceManagerEditor class of FairRoot v18.6.7
+
+#ifndef CbmTimesliceManagerEditor_H
+#define CbmTimesliceManagerEditor_H
+
+#include <Rtypes.h>      // for THashConsistencyHolder, ClassDef
+#include <RtypesCore.h>  // for Int_t, Bool_t, Double_t, UInt_t
+#include <TGFrame.h>     // for kChildFrame
+#include <TGedFrame.h>   // for TGedFrame
+
+#include <GuiTypes.h>  // for Pixel_t
+
+class CbmTimesliceManager;
+class CbmTsEveAnimationControl;
+class TBuffer;
+class TClass;
+class TClonesArray;
+class TGComboBox;
+class TGLabel;
+class TGNumberEntry;
+class TGWindow;
+class TMemberInspector;
+class TObject;
+
+class CbmTimesliceManagerEditor : public TGedFrame {
+public:
+  CbmTimesliceManagerEditor(const TGWindow* p = 0, Int_t width = 250, Int_t height = 30, UInt_t options = kChildFrame,
+                            Pixel_t back = GetDefaultFrameBackground());
+  virtual ~CbmTimesliceManagerEditor() = default;
+
+  CbmTimesliceManagerEditor(const CbmTimesliceManagerEditor&) = delete;
+  CbmTimesliceManagerEditor& operator=(const CbmTimesliceManagerEditor&) = delete;
+
+  /**
+    *
+    * @param light_background true if use white background
+    */
+  void SwitchBackground(Bool_t light_background);
+
+  virtual void Init();
+
+  virtual void SelectSingleTimeslice();
+  virtual void SelectTimeslice();
+  virtual void PrevTimeslice();
+  virtual void NextTimeslice();
+  virtual void SelectSingleEvent();
+  virtual void SelectEvent();
+  virtual void PrevEvent();
+  virtual void NextEvent();
+
+  void SetModel(TObject* obj);
+  virtual void StartAnimation();
+
+  /**
+    * make screenshot
+    */
+  void MakeScreenshot();
+
+protected:
+  TObject* fObject                   = nullptr;  //!
+  CbmTimesliceManager* fManager      = nullptr;  //!
+  TGNumberEntry* fCurrentTimeslice   = nullptr;  //!
+  TGNumberEntry* fCurrentEvent       = nullptr;  //!
+  TGNumberEntry* fGlobalTransparency = nullptr;  //!
+  TGLabel* fTimesliceTime            = nullptr;  //!
+  TGTextButton* fSelTs               = nullptr;  //!
+  TGTextButton* fPrevTs              = nullptr;  //!
+  TGTextButton* fNextTs              = nullptr;  //!
+  TGLabel* fEventNb                  = nullptr;  //!
+  TGLabel* fEventTime                = nullptr;  //!
+  TGTextButton* fUpdateEvent         = nullptr;  //!
+  TGTextButton* fPrevEvent           = nullptr;  //!
+  TGTextButton* fNextEvent           = nullptr;  //!
+
+  TGComboBox* fScreenshotOpt           = nullptr;  //!
+  CbmTsEveAnimationControl* fAnimation = nullptr;  //!
+
+  uint32_t fNbTs           = 0;
+  TClonesArray* fCbmEvents = nullptr;  //!
+  uint32_t fNbEventsInTs   = 0;
+
+  void SetTimesliceTimeLabel(Double_t time);
+  void SetEventTimeLabel(Double_t time);
+
+private:
+  ClassDef(CbmTimesliceManagerEditor, 1);
+};
+
+#endif  // CbmTimesliceManagerEditor_H
diff --git a/core/eventdisplay/CbmTimeslicePixelHitSetDraw.cxx b/core/eventdisplay/CbmTimeslicePixelHitSetDraw.cxx
new file mode 100644
index 0000000000..ef16878450
--- /dev/null
+++ b/core/eventdisplay/CbmTimeslicePixelHitSetDraw.cxx
@@ -0,0 +1,157 @@
+/* Copyright (C) 2009-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Florian Uhlig [committer] */
+
+#include "CbmTimeslicePixelHitSetDraw.h"
+
+#include "CbmEvent.h"             // for CbmEvent
+#include "CbmPixelHit.h"          // for CbmPixelHit
+#include "CbmTimesliceManager.h"  // for CbmTimesliceManager
+
+#include "FairDataSourceI.h"  // for FairDataSourceI
+#include "FairTCASource.h"    // for FairTCASource
+#include <Logger.h>           // for LOG, Logger
+
+#include <Rtypes.h>         // for ClassImp
+#include <TClonesArray.h>   // for TClonesArray
+#include <TEveManager.h>    // for TEveManager, gEve
+#include <TEvePointSet.h>   // for TEvePointSet
+#include <TEveTreeTools.h>  // for TEvePointSelectorConsumer, etc
+#include <TVector3.h>       // for TVector3
+
+CbmTimeslicePixelHitSetDraw::CbmTimeslicePixelHitSetDraw(const char* name, Color_t color, Style_t mstyle,
+                                                         Int_t iVerbose)
+  : FairPointSetDraw(name, color, mstyle, iVerbose)
+{
+  /// Trick to get access to the data source pointer even if private in the base class
+  /// TODO: switch from legacy constructor to standard constructor of the base class?
+  ///       or break inheritance and make this one standalone by copying the remaining parts of base class?
+  fLocalDataSourcePtr = new FairTCASource(GetName());
+  SetDataSource(fLocalDataSourcePtr);
+}
+
+InitStatus CbmTimeslicePixelHitSetDraw::Init()
+{
+  LOG(debug) << "CbmTimeslicePixelHitSetDraw::Init()";
+
+  /// Default initialization on base class
+  FairPointSetDraw::Init();
+
+  /// CBM timeslice specific: get array of CbmEvents
+  FairRootManager* fManager = FairRootManager::Instance();
+  fCbmEvents                = dynamic_cast<TClonesArray*>(fManager->GetObject("CbmEvent"));
+  if (nullptr == fCbmEvents) {
+    LOG(fatal) << "CbmTimeslicePixelHitSetDraw::Init() => CbmEvents branch not found! Task will be deactivated";
+    SetActive(kFALSE);
+  }
+
+  /// Find the data type enum based on the name provided to constructor
+  /// FIXME: find alternative to hard-coded if block... maybe reverse logic with constructor getting name from enum?
+  std::string sName = GetName();  // Needed for comparisoon with string literal
+  if ("MvdHit" == sName) {        //
+    fDataType = ECbmDataType::kMvdHit;
+  }
+  else if ("StsHit" == sName) {  //
+    fDataType = ECbmDataType::kStsHit;
+  }
+  else if ("RichHit" == sName) {  //
+    fDataType = ECbmDataType::kRichHit;
+  }
+  else if ("MuchHit" == sName) {  //
+    fDataType = ECbmDataType::kMuchPixelHit;
+  }
+  else if ("TrdHit" == sName) {  //
+    fDataType = ECbmDataType::kTrdHit;
+  }
+  else if ("TofHit" == sName) {  //
+    fDataType = ECbmDataType::kTofHit;
+  }
+  else if ("PsdHit" == sName) {  //
+    fDataType = ECbmDataType::kPsdHit;
+  }
+  else if ("T0Hit" == sName) {  //
+    fDataType = ECbmDataType::kT0Hit;
+  }
+  else {
+    fDataType = ECbmDataType::kUnknown;
+  }
+
+  if (IsActive()) { return kSUCCESS; }
+  else {
+    return kERROR;
+  }
+}
+
+void CbmTimeslicePixelHitSetDraw::Exec(Option_t* /*option*/)
+{
+  fLocalDataSourcePtr->Reset();
+  fLocalDataSourcePtr->RetrieveData(CbmTimesliceManager::Instance()->GetTimesliceTime());
+
+  if (0 < fCbmEvents->GetEntriesFast()) {
+    /// When loading a new TS, load the first event if possible
+    GotoEvent(0);
+  }
+}
+
+void CbmTimeslicePixelHitSetDraw::GotoEvent(uint32_t uEventIdx)
+{
+  LOG(debug) << "CbmTimeslicePixelHitSetDraw::GotoEvent " << uEventIdx << " target " << GetName();
+
+  if (fCbmEvents->GetEntriesFast() <= static_cast<Int_t>(uEventIdx)) {
+    LOG(fatal) << "CbmTimeslicePixelHitSetDraw::GotoEvent() => Failure, tried to load event " << uEventIdx
+               << " while only " << fCbmEvents->GetEntriesFast() << " events available in this TS!!!";
+  }
+
+  fEventIdx = uEventIdx;
+
+  CbmEvent* event = dynamic_cast<CbmEvent*>(fCbmEvents->At(uEventIdx));
+
+  int32_t iNbHitsInTs    = fLocalDataSourcePtr->GetNData();
+  int32_t iNbHitsInEvent = event->GetNofData(fDataType);
+
+  if (iNbHitsInTs < iNbHitsInEvent) {
+    LOG(fatal) << "CbmTimeslicePixelHitSetDraw::GotoEvent() => Failure, more " << GetName() << " in event " << uEventIdx
+               << " than available in the TS: " << iNbHitsInEvent << " VS " << iNbHitsInTs;
+  }
+
+  if (CbmTimesliceManager::Instance()->GetClearHandler()) {  //
+    Reset();
+  }
+
+  TEvePointSet* q = new TEvePointSet(GetName(), iNbHitsInEvent, TEvePointSelectorConsumer::kTVT_XYZ);
+  q->SetOwnIds(kTRUE);
+  q->SetMarkerColor(fColor);
+  q->SetMarkerSize(1.5);
+  q->SetMarkerStyle(fStyle);
+
+  for (int32_t iHitIdxInEvt = 0; iHitIdxInEvt < iNbHitsInEvent; ++iHitIdxInEvt) {
+    TVector3 vec(GetVector(fLocalDataSourcePtr->GetData(event->GetIndex(fDataType, iHitIdxInEvt))));
+    q->SetNextPoint(vec.X(), vec.Y(), vec.Z());
+    // q->SetPointId(GetValue(p, i));
+  }
+  gEve->AddElement(q);
+  gEve->Redraw3D(kFALSE);
+  fq = q;
+}
+
+void CbmTimeslicePixelHitSetDraw::Reset()
+{
+  if (fq != 0) {
+    fq->Reset();
+    gEve->RemoveElement(fq, CbmTimesliceManager::Instance());
+  }
+}
+
+
+TVector3 CbmTimeslicePixelHitSetDraw::GetVector(TObject* obj)
+{
+  CbmPixelHit* p = dynamic_cast<CbmPixelHit*>(obj);
+  if (nullptr == p) {
+    LOG(fatal) << "CbmTimesliceRecoTracks::GetVector() => Failure, object not derived from CbmPixelHit";
+  }
+  LOG(debug) << "-I- CbmTimeslicePixelHitSetDraw::GetVector: " << p->GetX() << " " << p->GetY() << " " << p->GetZ();
+  return TVector3(p->GetX(), p->GetY(), p->GetZ());
+}
+
+
+ClassImp(CbmTimeslicePixelHitSetDraw)
diff --git a/core/eventdisplay/CbmTimeslicePixelHitSetDraw.h b/core/eventdisplay/CbmTimeslicePixelHitSetDraw.h
new file mode 100644
index 0000000000..2fc7ef7cd1
--- /dev/null
+++ b/core/eventdisplay/CbmTimeslicePixelHitSetDraw.h
@@ -0,0 +1,42 @@
+/* Copyright (C) 2023 Facility for Antiproton and Ion Research in Europe, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau[committer] */
+
+#ifndef CbmTimeslicePixelHitSetDraw_H_
+#define CbmTimeslicePixelHitSetDraw_H_
+
+#include <CbmDefs.h>  // For ECbmDataType
+
+#include <FairPointSetDraw.h>  // for FairPointSetDraw
+
+#include <Rtypes.h>      // for THashConsistencyHolder, ClassDef
+#include <RtypesCore.h>  // for Color_t, Int_t, Style_t
+
+class TClonesArray;
+class TObject;
+class TVector3;
+
+class CbmTimeslicePixelHitSetDraw : public FairPointSetDraw {
+public:
+  CbmTimeslicePixelHitSetDraw(const char* name, Color_t color, Style_t mstyle, Int_t iVerbose = 1);
+  virtual ~CbmTimeslicePixelHitSetDraw() = default;
+
+  virtual InitStatus Init();
+  virtual void Exec(Option_t* option);
+  void Reset();
+
+  void GotoEvent(uint32_t uEventIdx);
+
+protected:
+  TVector3 GetVector(TObject* obj);
+
+private:
+  FairDataSourceI* fLocalDataSourcePtr = nullptr;                 //!
+  TClonesArray* fCbmEvents             = nullptr;                 //!
+  ECbmDataType fDataType               = ECbmDataType::kUnknown;  //!
+  uint32_t fEventIdx                   = 0;                       //!
+
+  ClassDef(CbmTimeslicePixelHitSetDraw, 1);
+};
+
+#endif /* CbmTimeslicePixelHitSetDraw_H_ */
diff --git a/core/eventdisplay/CbmTimesliceRecoTracks.cxx b/core/eventdisplay/CbmTimesliceRecoTracks.cxx
new file mode 100644
index 0000000000..b8ad83f856
--- /dev/null
+++ b/core/eventdisplay/CbmTimesliceRecoTracks.cxx
@@ -0,0 +1,250 @@
+/* Copyright (C) 2023 Facility for Antiproton and Ion Research in Europe, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau[committer] */
+#include "CbmTimesliceRecoTracks.h"
+
+#include "CbmEvent.h"             // For CbmEvent
+#include "CbmGlobalTrack.h"       // for CbmGlobalTrack
+#include "CbmHit.h"               // for HitType, kMUCHPIXELHIT, kRICHHIT
+#include "CbmPixelHit.h"          // for CbmPixelHit
+#include "CbmRichRing.h"          // for CbmRichRing
+#include "CbmStsTrack.h"          // for CbmStsTrack
+#include "CbmTrack.h"             // for CbmTrack
+#include <CbmTimesliceManager.h>  // for CbmTimesliceManager
+
+#include <FairRootManager.h>  // for FairRootManager
+#include <FairTask.h>         // for FairTask, InitStatus, kERROR, kSUCCESS
+#include <FairTrackParam.h>   // for FairTrackParam
+#include <Logger.h>           // for LOG, Logger
+
+#include <Rtypes.h>               // for ClassImp
+#include <TClonesArray.h>         // for TClonesArray
+#include <TEveManager.h>          // for TEveManager, gEve
+#include <TEvePathMark.h>         // for TEvePathMark
+#include <TEveTrack.h>            // for TEveTrackList, TEveTrack
+#include <TEveTrackPropagator.h>  // for TEveTrackPropagator
+#include <TEveVector.h>           // for TEveVector, TEveVectorT
+#include <TGenericClassInfo.h>    // for TGenericClassInfo
+#include <TParticle.h>            // for TParticle
+#include <TString.h>              // for TString
+#include <TVector3.h>             // for TVector3
+
+#include <stdio.h>   // for sprintf
+#include <string.h>  // for strcmp
+
+// -------------------------------------------------------------------------
+InitStatus CbmTimesliceRecoTracks::Init()
+{
+  LOG(debug) << "CbmTimesliceRecoTracks::Init()";
+  FairRootManager* fManager = FairRootManager::Instance();
+
+  fCbmEvents     = dynamic_cast<TClonesArray*>(fManager->GetObject("CbmEvent"));
+  fGlobalTracks  = dynamic_cast<TClonesArray*>(fManager->GetObject("GlobalTrack"));
+  fStsTracks     = dynamic_cast<TClonesArray*>(fManager->GetObject("StsTrack"));
+  fMvdHits       = dynamic_cast<TClonesArray*>(fManager->GetObject("MvdHit"));
+  fStsHits       = dynamic_cast<TClonesArray*>(fManager->GetObject("StsHit"));
+  fRichRings     = dynamic_cast<TClonesArray*>(fManager->GetObject("RichRing"));
+  fRichHits      = dynamic_cast<TClonesArray*>(fManager->GetObject("RichHit"));
+  fMuchPixelHits = dynamic_cast<TClonesArray*>(fManager->GetObject("MuchPixelHit"));
+  fMuchTracks    = dynamic_cast<TClonesArray*>(fManager->GetObject("MuchTrack"));
+  fTrdHits       = dynamic_cast<TClonesArray*>(fManager->GetObject("TrdHit"));
+  fTrdTracks     = dynamic_cast<TClonesArray*>(fManager->GetObject("TrdTrack"));
+  fTofHits       = dynamic_cast<TClonesArray*>(fManager->GetObject("TofHit"));
+  fTofTracks     = dynamic_cast<TClonesArray*>(fManager->GetObject("TofTrack"));
+
+  if (nullptr == fCbmEvents) {
+    LOG(fatal) << "CbmTimesliceRecoTracks::Init() => CbmEvents branch not found! Task will be deactivated";
+    SetActive(kFALSE);
+  }
+
+  LOG(debug1) << "CbmTimesliceRecoTracks::Init() get track list" << fStsTracks;
+  LOG(debug1) << "CbmTimesliceRecoTracks::Init()  create propagator";
+  fEventManager = CbmTimesliceManager::Instance();
+  LOG(debug1) << "CbmTimesliceRecoTracks::Init() got instance of CbmTimesliceManager ";
+
+  if (IsActive()) { return kSUCCESS; }
+  else {
+    return kERROR;
+  }
+}
+
+void CbmTimesliceRecoTracks::HandlePixelHit(TEveTrack* eveTrack, Int_t& n, const CbmPixelHit* hit, TEveVector* pMom = 0)
+{
+  eveTrack->SetPoint(n, hit->GetX(), hit->GetY(), hit->GetZ());
+  TEveVector pos = TEveVector(hit->GetX(), hit->GetY(), hit->GetZ());
+  TEvePathMark path;
+  path.fV    = pos;
+  path.fTime = 0;
+
+  if (pMom) path.fP = *pMom;
+
+  eveTrack->AddPathMark(path);
+  ++n;
+}
+
+void CbmTimesliceRecoTracks::HandleTrack(TEveTrack* eveTrack, Int_t& n, const CbmTrack* recoTrack)
+{
+  Int_t nofHits = recoTrack->GetNofHits();
+
+  for (Int_t i = 0; i < nofHits; ++i) {
+    HitType hitType             = recoTrack->GetHitType(i);
+    Int_t hitIndex              = recoTrack->GetHitIndex(i);
+    const CbmPixelHit* pixelHit = 0;
+
+    switch (hitType) {
+      case kRICHHIT: pixelHit = static_cast<const CbmPixelHit*>(fRichHits->At(hitIndex)); break;
+
+      case kMUCHPIXELHIT: pixelHit = static_cast<const CbmPixelHit*>(fMuchPixelHits->At(hitIndex)); break;
+
+      case kTRDHIT: pixelHit = static_cast<const CbmPixelHit*>(fTrdHits->At(hitIndex)); break;
+
+      case kTOFHIT: pixelHit = static_cast<const CbmPixelHit*>(fTofHits->At(hitIndex)); break;
+      default: LOG(warn) << "Pixel type " << hitType << " not supported.";
+    }
+
+    if (0 != pixelHit) HandlePixelHit(eveTrack, n, pixelHit);
+  }
+}
+
+void CbmTimesliceRecoTracks::HandleStsTrack(TEveTrack* eveTrack, Int_t& n, const CbmStsTrack* stsTrack)
+{
+  for (Int_t i = 0; i < stsTrack->GetNofMvdHits(); ++i) {
+    const CbmPixelHit* hit = static_cast<const CbmPixelHit*>(fMvdHits->At(stsTrack->GetMvdHitIndex(i)));
+
+    if (0 == n) {
+      TVector3 mom3;
+      stsTrack->GetParamFirst()->Momentum(mom3);
+      TEveVector mom = TEveVector(mom3.X(), mom3.Y(), mom3.Z());
+      HandlePixelHit(eveTrack, n, hit, &mom);
+    }
+    else
+      HandlePixelHit(eveTrack, n, hit);
+  }
+
+  for (Int_t i = 0; i < stsTrack->GetNofStsHits(); ++i) {
+    const CbmPixelHit* hit = static_cast<const CbmPixelHit*>(fStsHits->At(stsTrack->GetStsHitIndex(i)));
+
+    if (0 == n) {
+      TVector3 mom3;
+      stsTrack->GetParamFirst()->Momentum(mom3);
+      TEveVector mom = TEveVector(mom3.X(), mom3.Y(), mom3.Z());
+      HandlePixelHit(eveTrack, n, hit, &mom);
+    }
+    else
+      HandlePixelHit(eveTrack, n, hit);
+  }
+}
+
+void CbmTimesliceRecoTracks::Exec(Option_t* /*option*/)
+{
+  if (0 < fCbmEvents->GetEntriesFast()) {
+    /// When loading a new TS, load the first event if possible
+    GotoEvent(0);
+  }
+}
+void CbmTimesliceRecoTracks::GotoEvent(uint32_t uEventIdx)
+{
+  LOG(debug3) << "CbmTimesliceRecoTracks::GotoEvent " << uEventIdx;
+
+  if (fCbmEvents->GetEntriesFast() <= static_cast<Int_t>(uEventIdx)) {
+    LOG(fatal) << "CbmTimesliceRecoTracks::GotoEvent() => Failure, tried to load event " << uEventIdx << " while only "
+               << fCbmEvents->GetEntriesFast() << " events available in this TS!!!";
+  }
+
+  fEventIdx = uEventIdx;
+
+  if (CbmTimesliceManager::Instance()->GetClearHandler()) {  //
+    Reset();
+  }
+
+  CbmEvent* event = dynamic_cast<CbmEvent*>(fCbmEvents->At(uEventIdx));
+
+  Int_t nofGlobalTracks = event->GetNofData(ECbmDataType::kGlobalTrack);
+
+  for (Int_t iGloTrk = 0; iGloTrk < nofGlobalTracks; ++iGloTrk) {
+    LOG(debug3) << "CbmTimesliceRecoTracks::GotoEvent " << iGloTrk;
+    Int_t trkId                       = event->GetIndex(ECbmDataType::kGlobalTrack, iGloTrk);
+    const CbmGlobalTrack* globalTrack = dynamic_cast<const CbmGlobalTrack*>(fGlobalTracks->At(trkId));
+    Int_t stsId                       = globalTrack->GetStsTrackIndex();
+    Int_t richId                      = globalTrack->GetRichRingIndex();
+    Int_t muchId                      = globalTrack->GetMuchTrackIndex();
+    Int_t trdId                       = globalTrack->GetTrdTrackIndex();
+    Int_t tofId                       = globalTrack->GetTofTrackIndex();
+    Int_t tofHitId                    = globalTrack->GetTofHitIndex();
+
+    if (0 > stsId) continue;
+
+    const CbmStsTrack* stsTrack = dynamic_cast<const CbmStsTrack*>(fStsTracks->At(stsId));
+
+    if (0 == stsTrack) continue;
+
+    Int_t pdg = stsTrack->GetPidHypo();
+    TParticle P;
+    P.SetPdgCode(pdg);
+    fTrList             = GetTrGroup(&P);
+    TEveTrack* eveTrack = new TEveTrack(&P, pdg, fTrPr);
+    eveTrack->SetLineColor(fEventManager->Color(pdg));
+    Int_t n = 0;
+    HandleStsTrack(eveTrack, n, stsTrack);
+
+    if (-1 < richId) {
+      const CbmRichRing* r  = dynamic_cast<const CbmRichRing*>(fRichRings->At(richId));
+      const CbmPixelHit* rh = dynamic_cast<const CbmPixelHit*>(fRichHits->At(r->GetHit(0)));
+      CbmPixelHit h(*rh);
+      h.SetX(r->GetCenterX());
+      h.SetY(r->GetCenterY());
+      HandlePixelHit(eveTrack, n, &h);
+    }
+    else if (-1 < muchId) {
+      HandleTrack(eveTrack, n, dynamic_cast<const CbmTrack*>(fMuchTracks->At(muchId)));
+    }
+
+    if (-1 < trdId) HandleTrack(eveTrack, n, dynamic_cast<const CbmTrack*>(fTrdTracks->At(trdId)));
+
+    if (-1 < tofId) HandleTrack(eveTrack, n, dynamic_cast<const CbmTrack*>(fTofTracks->At(tofId)));
+    else if (-1 < tofHitId)
+      HandlePixelHit(eveTrack, n, dynamic_cast<const CbmPixelHit*>(fTofHits->At(tofHitId)));
+
+    /// Draw Markers of all hits participating in the track
+    eveTrack->SetRnrPoints(kTRUE);
+
+    fTrList->AddElement(eveTrack);
+    LOG(debug3) << "track added " << eveTrack->GetName();
+  }
+
+  gEve->Redraw3D(kFALSE);
+}
+// -------------------------------------------------------------------------
+void CbmTimesliceRecoTracks::Reset()
+{
+  for (Int_t i = 0; i < fEveTrList->GetEntriesFast(); i++) {
+    TEveTrackList* ele = (TEveTrackList*) fEveTrList->At(i);
+    gEve->RemoveElement(ele, fEventManager);
+  }
+  fEveTrList->Clear();
+}
+
+TEveTrackList* CbmTimesliceRecoTracks::GetTrGroup(TParticle* P)
+{
+  char name_buf[128];
+  sprintf(name_buf, "reco_%s", P->GetName());
+  fTrList = 0;
+  for (Int_t i = 0; i < fEveTrList->GetEntriesFast(); i++) {
+    TEveTrackList* TrListIn = (TEveTrackList*) fEveTrList->At(i);
+    if (strcmp(TrListIn->GetName(), name_buf) == 0) {
+      fTrList = TrListIn;
+      break;
+    }
+  }
+  if (fTrList == 0) {
+    fTrPr   = new TEveTrackPropagator();
+    fTrList = new TEveTrackList(name_buf, fTrPr);
+    fTrList->SetMainColor(fEventManager->Color(P->GetPdgCode()));
+    fEveTrList->Add(fTrList);
+    gEve->AddElement(fTrList, fEventManager);
+    fTrList->SetRnrLine(kTRUE);
+  }
+  return fTrList;
+}
+
+ClassImp(CbmTimesliceRecoTracks)
diff --git a/core/eventdisplay/CbmTimesliceRecoTracks.h b/core/eventdisplay/CbmTimesliceRecoTracks.h
new file mode 100644
index 0000000000..e92ba15c03
--- /dev/null
+++ b/core/eventdisplay/CbmTimesliceRecoTracks.h
@@ -0,0 +1,91 @@
+/* Copyright (C) 2023 Facility for Antiproton and Ion Research in Europe, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau[committer] */
+
+
+#ifndef CbmTimesliceRecoTracks_H
+#define CbmTimesliceRecoTracks_H
+
+#include <FairTask.h>  // for FairTask, InitStatus
+
+#include <Rtypes.h>      // for THashConsistencyHolder, ClassDef
+#include <RtypesCore.h>  // for Int_t, Double_t, Option_t
+#include <TEveVector.h>  // for TEveVector
+#include <TObjArray.h>   // for TObjArray, needed for FairRoot > v18.8.0
+#include <TString.h>     // for TString
+
+class CbmPixelHit;
+class CbmStsTrack;
+class CbmTrack;
+
+class TClonesArray;
+class TEveTrack;
+class TEveTrackList;
+class TEveTrackPropagator;
+class TObjArray;
+class TParticle;
+
+#ifndef CbmTimesliceManager_H
+class CbmTimesliceManager;
+#endif  // CbmTimesliceManager_H
+
+class CbmTimesliceRecoTracks : public FairTask {
+public:
+  /** Default constructor **/
+  CbmTimesliceRecoTracks() : CbmTimesliceRecoTracks("CbmTimesliceRecoTracks", 0) {}
+
+
+  /** Standard constructor
+    * @param name        Name of task
+    * @param iVerbose    Verbosity level
+    **/
+  CbmTimesliceRecoTracks(const char* name, Int_t iVerbose = 1) : FairTask(name, iVerbose) {}
+
+  /** Destructor **/
+  virtual ~CbmTimesliceRecoTracks() = default;
+
+  CbmTimesliceRecoTracks(const CbmTimesliceRecoTracks&) = delete;
+  CbmTimesliceRecoTracks& operator=(const CbmTimesliceRecoTracks&) = delete;
+
+  /** Set verbosity level. For this task and all of the subtasks. **/
+  void SetVerbose(Int_t iVerbose) { fVerbose = iVerbose; }
+
+  virtual InitStatus Init();
+  virtual void Exec(Option_t* option);
+  virtual void SetParContainers() { ; }
+  virtual void Finish() { ; }
+
+  void GotoEvent(uint32_t uEventIdx);
+  void Reset();
+  TEveTrackList* GetTrGroup(TParticle* P);
+
+protected:
+  void HandlePixelHit(TEveTrack* eveTrack, Int_t& n, const CbmPixelHit* hit, TEveVector* pMom);
+  void HandleTrack(TEveTrack* eveTrack, Int_t& n, const CbmTrack* recoTrack);
+  void HandleStsTrack(TEveTrack* eveTrack, Int_t& n, const CbmStsTrack* stsTrack);
+
+  TClonesArray* fCbmEvents           = nullptr;            //!
+  TClonesArray* fGlobalTracks        = nullptr;            //!
+  TClonesArray* fMvdHits             = nullptr;            //!
+  TClonesArray* fStsHits             = nullptr;            //!
+  TClonesArray* fStsTracks           = nullptr;            //!
+  TClonesArray* fRichRings           = nullptr;            //!
+  TClonesArray* fRichHits            = nullptr;            //!
+  TClonesArray* fMuchPixelHits       = nullptr;            //!
+  TClonesArray* fMuchTracks          = nullptr;            //!
+  TClonesArray* fTrdHits             = nullptr;            //!
+  TClonesArray* fTrdTracks           = nullptr;            //!
+  TClonesArray* fTofHits             = nullptr;            //!
+  TClonesArray* fTofTracks           = nullptr;            //!
+  TEveTrackPropagator* fTrPr         = nullptr;            //!
+  CbmTimesliceManager* fEventManager = nullptr;            //!
+  TObjArray* fEveTrList              = new TObjArray(16);  //!
+  uint32_t fEventIdx                 = 0;                  //!
+  TEveTrackList* fTrList             = nullptr;            //!
+
+private:
+  ClassDef(CbmTimesliceRecoTracks, 1);
+};
+
+
+#endif  // CbmTimesliceRecoTracks_H
diff --git a/core/eventdisplay/CbmTsEveAnimationControl.cxx b/core/eventdisplay/CbmTsEveAnimationControl.cxx
new file mode 100644
index 0000000000..3bdc030bb2
--- /dev/null
+++ b/core/eventdisplay/CbmTsEveAnimationControl.cxx
@@ -0,0 +1,264 @@
+/* Copyright (C) 2023 Facility for Antiproton and Ion Research in Europe, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau[committer] */
+
+/*
+ * FairEveAnimationControl.cxx
+ *
+ *  Created on: 26 maj 2020
+ *      Author: Daniel Wielanek
+ *		E-mail: daniel.wielanek@gmail.com
+ *		Warsaw University of Technology, Faculty of Physics
+ */
+/// PAL 01/06/2023: heavily based on FairEveAnimationControl from FairRoot v18.6.7, for usage with CbmTimesliceManager
+#include "CbmTsEveAnimationControl.h"
+
+CbmTsEveAnimationControl::CbmTsEveAnimationControl(TGedFrame* frame, TGCompositeFrame* tab, TString functionName,
+                                                   TString name, Int_t width)
+  : fWidth(width)
+  , fParent(frame)
+  , fFunctionName(functionName)
+  , fTab(tab)
+{
+  SetName(name);
+}
+
+CbmTsEveAnimationControl::~CbmTsEveAnimationControl() {}
+
+void CbmTsEveAnimationControl::Init()
+{
+  TGGroupFrame* animCtrl = new TGGroupFrame(fTab, GetName());
+  animCtrl->SetTitlePos(TGGroupFrame::kCenter);
+
+  // -------------------------------- Start "holy" button --------------------------------------------------------------
+  fStartButton = new TGTextButton(animCtrl, "Start");
+  fStartButton->Resize(80, 25);
+  animCtrl->AddFrame(fStartButton, new TGLayoutHints(kLHintsLeft | kLHintsExpandX, 5, 5, 5, 5));
+  fStartButton->Connect("Clicked()", fParent->ClassName(), fParent, fFunctionName);
+  fStartButton->SetEnabled(kFALSE);  // Enabled only if TS number set to at least 1
+  // -------------------------------------------------------------------------------------------------------------------
+
+  // -------------------------------- Animation type -------------------------------------------------------------------
+  TGCompositeFrame* frAnimType = new TGCompositeFrame(animCtrl, fWidth, 30, kHorizontalFrame | kFixedWidth);
+  TGLabel* gAnimType           = new TGLabel(frAnimType, "Animation type:");
+  frAnimType->AddFrame(gAnimType, new TGLayoutHints(kLHintsLeft | kLHintsExpandX, 1, 1, 1, 1));
+  fTypeOpt = new TGComboBox(frAnimType);
+  fTypeOpt->AddEntry("Evts in sel. TS", kEventsInTs);
+  fTypeOpt->AddEntry("Timeslices", kTimeSlices);
+  fTypeOpt->Select(kTimeSlices);
+  fTypeOpt->Resize(100, 20);
+  frAnimType->AddFrame(fTypeOpt, new TGLayoutHints(kLHintsLeft | kLHintsExpandX));
+  animCtrl->AddFrame(frAnimType, new TGLayoutHints(kLHintsLeft | kLHintsExpandX));
+  fTypeOpt->Connect("Selected (Int_t,Int_t)", this->ClassName(), this, "UpdateEnaDisButtons()");
+
+  TGCompositeFrame* frSelAnimFrameSec = new TGCompositeFrame(animCtrl, fWidth, 30, kHorizontalFrame | kFixedWidth);
+  TGLabel* gLabelFrameSec             = new TGLabel(frSelAnimFrameSec, "Frame length (s):");
+  frSelAnimFrameSec->AddFrame(gLabelFrameSec, new TGLayoutHints(kLHintsLeft | kLHintsExpandX, 1, 1, 2, 1));
+  fAnimFrameSec = new TGNumberEntry(frSelAnimFrameSec, 5.0, 6, -1, TGNumberFormat::kNESReal,
+                                    TGNumberFormat::kNEANonNegative, TGNumberFormat::kNELLimitMin, 0);
+  fAnimFrameSec->SetNumber(5.0);
+  frSelAnimFrameSec->AddFrame(fAnimFrameSec, new TGLayoutHints(kLHintsRight | kLHintsExpandX));
+  animCtrl->AddFrame(frSelAnimFrameSec, new TGLayoutHints(kLHintsLeft | kLHintsExpandX));
+  // -------------------------------------------------------------------------------------------------------------------
+
+  // -------------------------------- Event stepping buttons -----------------------------------------------------------
+  TGCompositeFrame* frSelEvtMin = new TGCompositeFrame(animCtrl, fWidth, 30, kHorizontalFrame | kFixedWidth);
+  TGLabel* gLabelEvtMin         = new TGLabel(frSelEvtMin, "Min Evt:");
+  frSelEvtMin->AddFrame(gLabelEvtMin, new TGLayoutHints(kLHintsLeft | kLHintsExpandX, 1, 1, 2, 1));
+  fEvtMin = new TGNumberEntry(frSelEvtMin, 0, 6, -1, TGNumberFormat::kNESInteger, TGNumberFormat::kNEANonNegative,
+                              TGNumberFormat::kNELLimitMinMax, 0, (fNbEvt ? fNbEvt - 1 : 0));
+  fEvtMin->SetNumber(0);
+  frSelEvtMin->AddFrame(fEvtMin, new TGLayoutHints(kLHintsRight | kLHintsExpandX));
+  animCtrl->AddFrame(frSelEvtMin, new TGLayoutHints(kLHintsLeft | kLHintsExpandX));
+
+  TGCompositeFrame* frSelEvtMax = new TGCompositeFrame(animCtrl, fWidth, 30, kHorizontalFrame | kFixedWidth);
+  TGLabel* gLabelEvtMax         = new TGLabel(frSelEvtMax, "Max Evt:");
+  frSelEvtMax->AddFrame(gLabelEvtMax, new TGLayoutHints(kLHintsLeft | kLHintsExpandX, 1, 1, 2, 1));
+  fEvtMax = new TGNumberEntry(frSelEvtMax, 10, 6, -1, TGNumberFormat::kNESInteger, TGNumberFormat::kNEANonNegative,
+                              TGNumberFormat::kNELLimitMinMax, 0, (fNbEvt ? fNbEvt - 1 : 0));
+  fEvtMax->SetNumber((fNbEvt ? fNbEvt - 1 : 0));
+  frSelEvtMax->AddFrame(fEvtMax, new TGLayoutHints(kLHintsRight | kLHintsExpandX));
+  animCtrl->AddFrame(frSelEvtMax, new TGLayoutHints(kLHintsLeft | kLHintsExpandX));
+
+  TGCompositeFrame* frSelEvtStep = new TGCompositeFrame(animCtrl, fWidth, 30, kHorizontalFrame | kFixedWidth);
+  TGLabel* gLabelEvtStep         = new TGLabel(frSelEvtStep, "Step Evt:");
+  frSelEvtStep->AddFrame(gLabelEvtStep, new TGLayoutHints(kLHintsLeft | kLHintsExpandX, 1, 1, 2, 1));
+  fEvtStep = new TGNumberEntry(frSelEvtStep, 0, 6, -1, TGNumberFormat::kNESInteger, TGNumberFormat::kNEAPositive,
+                               TGNumberFormat::kNELLimitMinMax, 1, (fNbEvt ? fNbEvt - 1 : 1));
+  fEvtStep->SetNumber(1);
+  frSelEvtStep->AddFrame(fEvtStep, new TGLayoutHints(kLHintsRight | kLHintsExpandX));
+  animCtrl->AddFrame(frSelEvtStep, new TGLayoutHints(kLHintsLeft | kLHintsExpandX));
+
+  /// In Timeslice mode, scan anyway on all events in each TS, in event mode wait for loading of first TS
+  /// Not SetEnabled for TGNumberEntry, guessing equivalent is SetState
+  fEvtMin->SetState(kFALSE);
+  fEvtMax->SetState(kFALSE);
+  fEvtStep->SetState(kFALSE);
+  // -------------------------------------------------------------------------------------------------------------------
+
+  // -------------------------------- TS stepping buttons --------------------------------------------------------------
+  TGCompositeFrame* frSelTsMin = new TGCompositeFrame(animCtrl, fWidth, 30, kHorizontalFrame | kFixedWidth);
+  TGLabel* gLabelTsMin         = new TGLabel(frSelTsMin, "Min Ts:");
+  frSelTsMin->AddFrame(gLabelTsMin, new TGLayoutHints(kLHintsLeft | kLHintsExpandX, 1, 1, 2, 1));
+  fTsMin = new TGNumberEntry(frSelTsMin, 0, 6, -1, TGNumberFormat::kNESInteger, TGNumberFormat::kNEANonNegative,
+                             TGNumberFormat::kNELLimitMinMax, 0, (fNbTs ? fNbTs - 1 : 0));
+  fTsMin->SetNumber(0);
+  frSelTsMin->AddFrame(fTsMin, new TGLayoutHints(kLHintsRight | kLHintsExpandX));
+  animCtrl->AddFrame(frSelTsMin, new TGLayoutHints(kLHintsLeft | kLHintsExpandX));
+
+  TGCompositeFrame* frSelTsMax = new TGCompositeFrame(animCtrl, fWidth, 30, kHorizontalFrame | kFixedWidth);
+  TGLabel* gLabelTsMax         = new TGLabel(frSelTsMax, "Max Ts:");
+  frSelTsMax->AddFrame(gLabelTsMax, new TGLayoutHints(kLHintsLeft | kLHintsExpandX, 1, 1, 2, 1));
+  fTsMax = new TGNumberEntry(frSelTsMax, 10, 6, -1, TGNumberFormat::kNESInteger, TGNumberFormat::kNEANonNegative,
+                             TGNumberFormat::kNELLimitMinMax, 0, (fNbTs ? fNbTs - 1 : 0));
+  fTsMax->SetNumber((fNbTs ? fNbTs - 1 : 0));
+  frSelTsMax->AddFrame(fTsMax, new TGLayoutHints(kLHintsRight | kLHintsExpandX));
+  animCtrl->AddFrame(frSelTsMax, new TGLayoutHints(kLHintsLeft | kLHintsExpandX));
+
+  TGCompositeFrame* frSelTsStep = new TGCompositeFrame(animCtrl, fWidth, 30, kHorizontalFrame | kFixedWidth);
+  TGLabel* gLabelTsStep         = new TGLabel(frSelTsStep, "Step Ts:");
+  frSelTsStep->AddFrame(gLabelTsStep, new TGLayoutHints(kLHintsLeft | kLHintsExpandX, 1, 1, 2, 1));
+  fTsStep = new TGNumberEntry(frSelTsStep, 0, 6, -1, TGNumberFormat::kNESInteger, TGNumberFormat::kNEAPositive,
+                              TGNumberFormat::kNELLimitMinMax, 1, (fNbTs ? fNbTs - 1 : 1));
+  fTsStep->SetNumber(1);
+  frSelTsStep->AddFrame(fTsStep, new TGLayoutHints(kLHintsRight | kLHintsExpandX));
+  animCtrl->AddFrame(frSelTsStep, new TGLayoutHints(kLHintsLeft | kLHintsExpandX));
+
+  Bool_t bAtLeastOneTs = (0 < fNbTs);
+  fTsMin->SetState(bAtLeastOneTs);
+  fTsMax->SetState(bAtLeastOneTs);
+  fTsStep->SetState(bAtLeastOneTs);
+  fEvtStep->SetState(bAtLeastOneTs);
+  fStartButton->SetEnabled(bAtLeastOneTs);
+  // -------------------------------------------------------------------------------------------------------------------
+
+  // -------------------------------- Screenshot Ena & type ------------------------------------------------------------
+  fBtnScreenshotEna = new TGCheckButton(animCtrl, "Screenshot each event");
+  fBtnScreenshotEna->SetOn();
+  animCtrl->AddFrame(fBtnScreenshotEna, new TGLayoutHints(kLHintsLeft | kLHintsExpandX));
+  fBtnScreenshotEna->Connect("Clicked()", this->ClassName(), this, "UpdateEnaScreenshots()");
+
+  TGCompositeFrame* frSceneType = new TGCompositeFrame(animCtrl, fWidth, 30, kHorizontalFrame | kFixedWidth);
+  TGLabel* gLabelType           = new TGLabel(frSceneType, "Scene type:");
+  frSceneType->AddFrame(gLabelType, new TGLayoutHints(kLHintsLeft | kLHintsExpandX, 1, 1, 2, 1));
+  fSceneOpt = new TGComboBox(frSceneType);
+  fSceneOpt->AddEntry("3D", k3D);
+  if (fbMcbmViewersEna) {
+    fSceneOpt->AddEntry("ZX", kZX);
+    fSceneOpt->AddEntry("ZY", kZY);
+  }
+  else {
+    fSceneOpt->AddEntry("RPhi", kXY);
+    fSceneOpt->AddEntry("RhoZ", kZ);
+  }
+  fSceneOpt->AddEntry("All", kAll);
+  fSceneOpt->Select(k3D);
+  fSceneOpt->Resize(100, 20);
+  frSceneType->AddFrame(fSceneOpt, new TGLayoutHints(kLHintsLeft | kLHintsExpandX));
+  animCtrl->AddFrame(frSceneType, new TGLayoutHints(kLHintsLeft | kLHintsExpandX));
+  // -------------------------------------------------------------------------------------------------------------------
+
+  // -------------------------------- Display clear ctrl ---------------------------------------------------------------
+  fBtnClearBuffer = new TGCheckButton(animCtrl, "Clear Buffer at Start");
+  fBtnClearBuffer->SetOn();
+  animCtrl->AddFrame(fBtnClearBuffer, new TGLayoutHints(kLHintsLeft | kLHintsExpandX));
+
+  fBtnRunContinuous = new TGCheckButton(animCtrl, "Run Continuous (stack events)");
+  fBtnRunContinuous->SetOn(kFALSE);
+  animCtrl->AddFrame(fBtnRunContinuous, new TGLayoutHints(kLHintsLeft | kLHintsExpandX));
+  // -------------------------------------------------------------------------------------------------------------------
+
+  fTab->AddFrame(animCtrl, new TGLayoutHints(kLHintsRight | kLHintsExpandX, 1, 1, 2, 1));
+
+  UpdateTsLimits();
+
+  fbInitDone = true;
+}
+
+CbmTsEveAnimationControl::eAnimationType CbmTsEveAnimationControl::GetAnimationType() const
+{
+  return static_cast<eAnimationType>(fTypeOpt->GetSelected());
+}
+
+Double_t CbmTsEveAnimationControl::GetAnimFrameSec() { return fAnimFrameSec->GetNumber(); }
+
+Bool_t CbmTsEveAnimationControl::GetScreenshotEna() { return fBtnScreenshotEna->IsOn(); }
+
+CbmTsEveAnimationControl::eScreenshotType CbmTsEveAnimationControl::GetScreenshotType() const
+{
+  return static_cast<eScreenshotType>(fSceneOpt->GetSelected());
+}
+
+Int_t CbmTsEveAnimationControl::GetEventMin() { return fEvtMin->GetNumber(); }
+
+Int_t CbmTsEveAnimationControl::GetEventMax() { return fEvtMax->GetNumber(); }
+
+Int_t CbmTsEveAnimationControl::GetEventStep() { return fEvtStep->GetNumber(); }
+
+Int_t CbmTsEveAnimationControl::GetTsMin() { return fTsMin->GetNumber(); }
+
+Int_t CbmTsEveAnimationControl::GetTsMax() { return fTsMax->GetNumber(); }
+
+Int_t CbmTsEveAnimationControl::GetTsStep() { return fTsStep->GetNumber(); }
+
+Bool_t CbmTsEveAnimationControl::GetRunContinuous() { return fBtnRunContinuous->IsOn(); }
+
+Bool_t CbmTsEveAnimationControl::GetClearBuffer() { return fBtnClearBuffer->IsOn(); }
+
+void CbmTsEveAnimationControl::UpdateEnaScreenshots() { fSceneOpt->SetEnabled(fBtnScreenshotEna->IsOn()); }
+
+void CbmTsEveAnimationControl::UpdateEnaDisButtons()
+{
+  /// Prevent invalid accesses
+  Bool_t bAtLeastOneTs = (0 < fNbTs);
+  if (bAtLeastOneTs) {
+    fStartButton->SetEnabled(kTRUE);
+
+    Bool_t bTsScanMode = (kTimeSlices == GetAnimationType());
+
+    /// In Timeslice mode, scan anyway on all events in each TS in range
+    fEvtMin->SetState(!bTsScanMode);
+    fEvtMax->SetState(!bTsScanMode);
+    fEvtStep->SetState(kTRUE);
+
+    /// In Event mode, scan only on events within selected TS
+    fTsMin->SetState(bTsScanMode);
+    fTsMax->SetState(bTsScanMode);
+    fTsStep->SetState(bTsScanMode);
+  }
+  else {
+    fStartButton->SetEnabled(kFALSE);
+    fEvtMin->SetState(kFALSE);
+    fEvtMax->SetState(kFALSE);
+    fEvtStep->SetState(kFALSE);
+    fTsMin->SetState(kFALSE);
+    fTsMax->SetState(kFALSE);
+    fTsStep->SetState(kFALSE);
+  }
+}
+
+void CbmTsEveAnimationControl::UpdateEventLimits()
+{
+  fEvtMin->SetLimitValues(0, fNbEvt - 1);
+  fEvtMax->SetLimitValues(0, fNbEvt - 1);
+  fEvtStep->SetLimitValues(1, fNbEvt - 1);
+
+  fEvtMin->SetNumber(0);
+  fEvtMax->SetNumber(fNbEvt - 1);
+  fEvtStep->SetNumber(1);
+
+  UpdateEnaDisButtons();
+}
+
+void CbmTsEveAnimationControl::UpdateTsLimits()
+{
+  fTsMin->SetLimitValues(0, fNbTs - 1);
+  fTsMax->SetLimitValues(0, fNbTs - 1);
+  fTsStep->SetLimitValues(1, fNbTs - 1);
+
+  fTsMin->SetNumber(0);
+  fTsMax->SetNumber(fNbTs - 1);
+  fTsStep->SetNumber(1);
+
+  UpdateEnaDisButtons();
+}
diff --git a/core/eventdisplay/CbmTsEveAnimationControl.h b/core/eventdisplay/CbmTsEveAnimationControl.h
new file mode 100644
index 0000000000..1a55499587
--- /dev/null
+++ b/core/eventdisplay/CbmTsEveAnimationControl.h
@@ -0,0 +1,129 @@
+/* Copyright (C) 2023 Facility for Antiproton and Ion Research in Europe, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau[committer] */
+
+/*
+ * FairEveAnimationControl.h
+ *
+ *  Created on: 26 maj 2020
+ *      Author: Daniel Wielanek
+ *		E-mail: daniel.wielanek@gmail.com
+ *		Warsaw University of Technology, Faculty of Physics
+ */
+/// PAL 01/06/2023: Heavily based on FairEveAnimationControl from FairRoot v18.6.7, for usage with CbmTimesliceManager
+
+#ifndef CBMTSEVEANIMATIONCONTROL_H_
+#define CBMTSEVEANIMATIONCONTROL_H_
+
+#include <Rtypes.h>
+#include <RtypesCore.h>
+#include <TGButton.h>
+#include <TGButton.h>  // for TGTextButton, TGCheckButton
+#include <TGComboBox.h>
+#include <TGDoubleSlider.h>
+#include <TGFrame.h>
+#include <TGLabel.h>
+#include <TGNumberEntry.h>
+#include <TGedFrame.h>  // for TGedFrame
+#include <TObject.h>
+
+#include <GuiTypes.h>
+
+class CbmTsEveAnimationControl : public TNamed {
+public:
+  enum eAnimationType
+  {
+    kEventsInTs = 0,
+    kTimeSlices = 1
+  };
+  enum eScreenshotType
+  {
+    kAll = 0,
+    k3D  = 1,
+    kZX  = 2,
+    kZY  = 3,
+    kXY  = 4,
+    kZ   = 5
+  };
+  CbmTsEveAnimationControl(TGedFrame* frame, TGCompositeFrame* tab, TString functionName, TString name = "",
+                           Int_t width = 170);
+
+  virtual ~CbmTsEveAnimationControl();
+
+
+  void SetDisplayMcbm(bool bEna) { fbMcbmViewersEna = bEna; }
+
+  /**
+   * set name of function called when button is pressed
+   * @param name
+   */
+  void SetFunctionName(TString name) { fFunctionName = name; };
+
+  void SetEvtNb(uint32_t uNbEvt)
+  {
+    fNbEvt = uNbEvt;
+
+    if (fbInitDone) {  //
+      UpdateEventLimits();
+    }
+  };
+
+  void SetTsNb(uint32_t uTsNb)
+  {
+    fNbTs = uTsNb;
+
+    if (fbInitDone) {  //
+      UpdateTsLimits();
+    }
+  };
+
+  void Init();
+
+  eAnimationType GetAnimationType() const;
+  Double_t GetAnimFrameSec();
+  Bool_t GetScreenshotEna();
+  eScreenshotType GetScreenshotType() const;
+  Int_t GetEventMin();
+  Int_t GetEventMax();
+  Int_t GetEventStep();
+  Int_t GetTsMin();
+  Int_t GetTsMax();
+  Int_t GetTsStep();
+  Bool_t GetRunContinuous();
+  Bool_t GetClearBuffer();
+
+  void UpdateEnaScreenshots();
+  void UpdateEnaDisButtons();
+  void UpdateEventLimits();
+  void UpdateTsLimits();
+
+private:
+  const Int_t fWidth;
+  uint32_t fNbEvt = 0;
+  uint32_t fNbTs  = 0;
+
+  TGedFrame* fParent    = nullptr;
+  TString fFunctionName = "";
+
+  bool fbMcbmViewersEna = false;  //!
+  bool fbInitDone       = false;
+
+  TGCompositeFrame* fTab           = nullptr;
+  TGTextButton* fStartButton       = nullptr;
+  TGComboBox* fTypeOpt             = nullptr;
+  TGNumberEntry* fAnimFrameSec     = nullptr;
+  TGCheckButton* fBtnScreenshotEna = nullptr;
+  TGComboBox* fSceneOpt            = nullptr;
+  TGNumberEntry* fEvtMin           = nullptr;
+  TGNumberEntry* fEvtMax           = nullptr;
+  TGNumberEntry* fEvtStep          = nullptr;
+  TGNumberEntry* fTsMin            = nullptr;
+  TGNumberEntry* fTsMax            = nullptr;
+  TGNumberEntry* fTsStep           = nullptr;
+  TGCheckButton* fBtnRunContinuous = nullptr;
+  TGCheckButton* fBtnClearBuffer   = nullptr;
+
+  ClassDef(CbmTsEveAnimationControl, 1)
+};
+
+#endif /* CBMTSEVEANIMATIONCONTROL_H_ */
diff --git a/core/eventdisplay/CbmTsEveTransparencyControl.cxx b/core/eventdisplay/CbmTsEveTransparencyControl.cxx
new file mode 100644
index 0000000000..6b58040aeb
--- /dev/null
+++ b/core/eventdisplay/CbmTsEveTransparencyControl.cxx
@@ -0,0 +1,52 @@
+/* Copyright (C) 2023 Facility for Antiproton and Ion Research in Europe, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau[committer] */
+
+#include "CbmTsEveTransparencyControl.h"
+
+#include "CbmTimesliceManager.h"  // for CbmTimesliceManager
+
+#include <TGButton.h>       // for TGCheckButton
+#include <TGNumberEntry.h>  // for TGNumberEntry, TGNumberFormat, TGNumbe...
+
+CbmTsEveTransparencyControl::CbmTsEveTransparencyControl(TGFrame const* parent, char const* label)
+  : TGHorizontalFrame(parent)
+  , fCheck(new TGCheckButton(this, label))
+  , fNumber(new TGNumberEntry(this,
+                              80.,  // initial number
+                              6,    // digitwidth
+                              -1, TGNumberFormat::kNESInteger, TGNumberFormat::kNEANonNegative,
+                              TGNumberFormat::kNELLimitMinMax,
+                              0,     // min
+                              100))  // max
+{
+  SetCleanup(kDeepCleanup);
+
+  // display
+  AddFrame(fCheck);   // takes ownership
+  AddFrame(fNumber);  // takes ownership
+
+  // wire up observers
+  fCheck->Connect("Toggled(Bool_t)", this->ClassName(), this, "Toggled()");
+  fNumber->Connect("ValueSet(Long_t)", this->ClassName(), this, "ValueSet()");
+}
+
+void CbmTsEveTransparencyControl::Toggled()
+{
+  if (fCheck->IsOn()) {  //
+    CbmTimesliceManager::Instance()->SetTransparency(kFALSE, fNumber->GetIntNumber());
+  }
+  else {
+    CbmTimesliceManager::Instance()->SetTransparency(kTRUE, fNumber->GetIntNumber());
+  }
+}
+
+void CbmTsEveTransparencyControl::ValueSet()
+{
+  if (fCheck->IsOn()) {  //
+    CbmTimesliceManager::Instance()->SetTransparency(kFALSE, fNumber->GetIntNumber());
+  }
+  else {
+    CbmTimesliceManager::Instance()->SetTransparency(kTRUE, fNumber->GetIntNumber());
+  }
+}
diff --git a/core/eventdisplay/CbmTsEveTransparencyControl.h b/core/eventdisplay/CbmTsEveTransparencyControl.h
new file mode 100644
index 0000000000..bc4e86dbf8
--- /dev/null
+++ b/core/eventdisplay/CbmTsEveTransparencyControl.h
@@ -0,0 +1,37 @@
+/* Copyright (C) 2023 Facility for Antiproton and Ion Research in Europe, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau[committer] */
+
+/// PAL 31/05/2023: clone of FairEveTransparencyControl from FairRoot v18.6.7 for usage with CbmTimesliceManager
+
+#ifndef CBMTSEVETRANSPARENCYCONTROL_H_
+#define CBMTSEVETRANSPARENCYCONTROL_H_
+
+#include <Rtypes.h>      // for THashConsistencyHolder, ClassDef
+#include <RtypesCore.h>  // for Bool_t
+#include <TGFrame.h>     // for TGFrame (ptr only), TGHorizontalFrame
+class TBuffer;
+class TClass;
+class TGCheckButton;
+class TGNumberEntry;  // lines 16-16
+class TMemberInspector;
+
+class CbmTsEveTransparencyControl : public TGHorizontalFrame {
+  TGCheckButton* fCheck;
+  TGNumberEntry* fNumber;
+
+public:
+  CbmTsEveTransparencyControl(TGFrame const* parent, char const* label = "Transparency");
+
+  TGCheckButton* GetCheck() const { return fCheck; }
+  TGNumberEntry* GetNumber() const { return fNumber; }
+
+  void Toggled();   // SLOT to receive check button events
+  void ValueSet();  // SLOT to receive number entry events
+
+  virtual ~CbmTsEveTransparencyControl() {};
+
+  ClassDef(CbmTsEveTransparencyControl, 1)
+};
+
+#endif /* CBMTSEVETRANSPARENCYCONTROL_H_ */
diff --git a/macro/.gitignore b/macro/.gitignore
new file mode 100644
index 0000000000..1b96abf53a
--- /dev/null
+++ b/macro/.gitignore
@@ -0,0 +1,2 @@
+# Ignore file auto-created by event displays (probably coming from FairRoot or Root)
+**/temp_event_display.root
diff --git a/macro/beamtime/mcbm2022/event_display_l1reco.C b/macro/beamtime/mcbm2022/event_display_l1reco.C
new file mode 100644
index 0000000000..a801f046d4
--- /dev/null
+++ b/macro/beamtime/mcbm2022/event_display_l1reco.C
@@ -0,0 +1,104 @@
+/* Copyright (C) 2023 Facility for Antiproton and Ion Research in Europe, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau[committer] */
+
+/// FIXME: add support for alignment file
+
+void event_display_l1reco(uint32_t uRunId, std::string sRecoFile, std::string sGeoFile, std::string sAlignFile = "",
+                          std::string sXmlGeoConfig = "", std::string sUnpFile = "")
+{
+  // -----   Logger settings   -------------------------------------------------------------------------------------- //
+  FairLogger::GetLogger()->SetLogScreenLevel("INFO");
+  fair::Logger::DefineVerbosity(
+    "user1", fair::VerbositySpec::Make(fair::VerbositySpec::Info::severity, fair::VerbositySpec::Info::file_line));
+  FairLogger::GetLogger()->SetLogVerbosityLevel("user1");
+  // ---------------------------------------------------------------------------------------------------------------- //
+
+
+  // -----   CbmSetup   ----------------------------------------------------------------------------------------------------------------- //
+  /// Do automatic mapping only if not overridden by user or empty
+  cbm::mcbm::ToForceLibLoad dummy;  /// Needed to trigger loading of the library as no fct dict in ROOT6 and CLING
+  TString geoSetupTag = "";
+  try {
+    geoSetupTag = cbm::mcbm::GetSetupFromRunId(uRunId);
+  }
+  catch (const std::invalid_argument& e) {
+    std::cout << "Error in mapping from runID to setup name: " << e.what() << std::endl;
+    return kFALSE;
+  }
+
+  auto geoSetup = CbmSetup::Instance();
+  geoSetup->LoadSetup(geoSetupTag);
+
+  // You can modify the pre-defined setup by using
+  geoSetup->SetActive(ECbmModuleId::kMvd, kFALSE);
+  geoSetup->SetActive(ECbmModuleId::kSts, kTRUE);
+  geoSetup->SetActive(ECbmModuleId::kMuch, kFALSE);
+  geoSetup->SetActive(ECbmModuleId::kRich, kTRUE);
+  geoSetup->SetActive(ECbmModuleId::kTrd, kTRUE);
+  geoSetup->SetActive(ECbmModuleId::kTrd2d, kTRUE);
+  geoSetup->SetActive(ECbmModuleId::kTof, kTRUE);
+  geoSetup->SetActive(ECbmModuleId::kPsd, kFALSE);
+  // ---------------------------------------------------------------------------------------------------------------- //
+
+  // -----   Reconstruction run   ----------------------------------------------------------------------------------- //
+  FairRunAna* fRun = new FairRunAna();
+
+  FairFileSource* inputSource = new FairFileSource(sRecoFile);
+  if ("" != sUnpFile) inputSource->AddFriend(sUnpFile);
+  fRun->SetSource(inputSource);
+
+  FairRootFileSink* outputSink = new FairRootFileSink("temp_event_display.root");
+  fRun->SetSink(outputSink);
+
+  fRun->SetGeomFile(sGeoFile.data());
+
+  if ("" != sAlignFile) {
+    std::cout << "Applying alignment for file " << sAlignFile << std::endl;
+
+    // Define the basic structure which needs to be filled with information
+    // This structure is stored in the output file and later passed to the
+    // FairRoot framework to do the (miss)alignment
+    std::map<std::string, TGeoHMatrix>* matrices {nullptr};
+
+    // read matrices from disk
+    TFile* misalignmentMatrixRootfile = new TFile(sAlignFile.data(), "READ");
+    if (misalignmentMatrixRootfile->IsOpen()) {
+      gDirectory->GetObject("MisalignMatrices", matrices);
+      misalignmentMatrixRootfile->Close();
+    }
+    else {
+      std::cout << "Could not open file " << sAlignFile << "\n Exiting";
+      return kFALSE;
+    }
+
+    if (matrices) { fRun->AddAlignmentMatrices(*matrices); }
+    else {
+      LOG(error) << "Alignment required but no matrices found."
+                 << "\n Exiting";
+      return kFALSE;
+    }
+  }
+  // ---------------------------------------------------------------------------------------------------------------- //
+
+  // -----   Event display   ---------------------------------------------------------------------------------------- //
+  CbmTimesliceManager* fMan = new CbmTimesliceManager();
+  fMan->SetXMLConfig(sXmlGeoConfig);
+  fMan->SetDisplayMcbm();
+  // ---------------------------------------------------------------------------------------------------------------- //
+
+  fMan->Init(1, 7);  // Make all sensors visible, finer tuning needs to be done in XML file
+  // fMan->Init(1, 4);
+  // fMan->Init(1, 5);  //make TPF and TRD visible by default
+  // fMan->Init(1, 6);  // make STS sensors visible by default
+  // fMan->Init(1, 7);  // make RICH sensors visible by default
+
+  //-------------- NH display macro --------------------------------------------------------------------------------- //
+  cout << "customize TEveManager gEve " << gEve << endl;
+  gEve->GetDefaultGLViewer()->SetClearColor(kYellow - 10);
+  TGLViewer* v       = gEve->GetDefaultGLViewer();
+  TGLAnnotation* ann = new TGLAnnotation(v, Form("%u", uRunId), 0.01, 0.98);
+  ann->SetTextSize(0.03);  // % of window diagonal
+  ann->SetTextColor(4);
+  // ---------------------------------------------------------------------------------------------------------------- //
+}
diff --git a/macro/beamtime/mcbm2022/evt_disp_conf_mcbm_beam_2022_05_23_nickel.xml b/macro/beamtime/mcbm2022/evt_disp_conf_mcbm_beam_2022_05_23_nickel.xml
new file mode 100644
index 0000000000..146379efe4
--- /dev/null
+++ b/macro/beamtime/mcbm2022/evt_disp_conf_mcbm_beam_2022_05_23_nickel.xml
@@ -0,0 +1,48 @@
+<xmlconf>
+<!--
+- color is handled by the FairEventManager, converted from text to ROOT colors, ignored if "", recursive value is depth
+- transparency is handled by the FairEventManager, should be between 1 and 99, ignored if "", recursive value is depth
+  (0 and 100 theoritically, see https://root-forum.cern.ch/t/make-a-geometry-transparent/47665/9)
+- Visibility is boolean and applied to all daughters if recursive set >0
+  => To disable a full detector apart from a few leaves,
+     1) set the top node to "visibility = 0, recursive = 1"
+     2) set each intermediate node to "visibility = 0, recursive = 1"
+     3) set the nodes that should be visible to "visibility = 1, recursive = 1"
+-->
+<Detectors>
+    <detector name="cave_1" color="" transparency="" visibility="" recursive="0">
+        <detector name="pipe_v19f_0" color="" transparency="95" visibility="" recursive="1"></detector>
+        <detector name="sts_v22e_mcbm_0" color="" transparency="" visibility="0" recursive="1">
+            <detector name="Station01_1" color="" transparency="" visibility="1" recursive="1"></detector>
+            <detector name="Station02_2" color="" transparency="" visibility="1" recursive="1"></detector>
+        </detector>
+        <detector name="trd_v22h_mcbm_0" color="" transparency="" visibility="0" recursive="1">
+            <detector name="layer01_20101" color="" transparency="" visibility="0" recursive="1">
+                <detector name="module9_101001001" color="" transparency="" visibility="0" recursive="1">
+                    <detector name="PadPlane_1" color="" transparency="" visibility="1" recursive="1"></detector>
+                </detector>
+            </detector>
+            <detector name="layer02_10202" color="" transparency="" visibility="0" recursive="1">
+                <detector name="module8_101002001" color="" transparency="" visibility="0" recursive="1">
+                    <detector name="padcopper_1" color="" transparency="" visibility="1" recursive="1"></detector>
+                    <detector name="padplane_1" color="" transparency="" visibility="1" recursive="1"></detector>
+                </detector>
+            </detector>
+            <detector name="layer03_11303" color="" transparency="" visibility="0" recursive="1">
+                <detector name="module8_101303001" color="" transparency="" visibility="0" recursive="1">
+                    <detector name="padcopper_1" color="" transparency="" visibility="1" recursive="1"></detector>
+                    <detector name="padplane_1" color="" transparency="" visibility="1" recursive="1"></detector>
+                </detector>
+            </detector>
+        </detector>
+        <detector name="rich_v21c_mcbm_0" color="" transparency="" visibility="0" recursive="1">
+            <detector name="box_1" color="" transparency="" visibility="0" recursive="1">
+                <detector name="Gas_1" color="" transparency="" visibility="0" recursive="1">
+                    <detector name="pmt_plane_1" color="" transparency="" visibility="1" recursive="1"></detector>
+                </detector>
+            </detector>
+        </detector>
+        <detector name="platform_v20a_mcbm_0" color="" transparency="" visibility="0" recursive="1"></detector>
+    </detector>
+</Detectors>
+</xmlconf>
diff --git a/macro/run/event_display_reco_ts.C b/macro/run/event_display_reco_ts.C
new file mode 100644
index 0000000000..a2ab346f05
--- /dev/null
+++ b/macro/run/event_display_reco_ts.C
@@ -0,0 +1,89 @@
+/* Copyright (C) 2023 Facility for Antiproton and Ion Research in Europe, Darmstadt
+   SPDX-License-Identifier: GPL-3.0-only
+   Authors: Pierre-Alain Loizeau[committer] */
+
+void event_display_reco_ts(TString geoSetupTag, std::string sRecoFile, std::string sGeoFile,
+                           std::string sAlignFile = "", std::string sXmlGeoConfig = "", std::string sUnpFile = "")
+{
+  // -----   Logger settings   -------------------------------------------------------------------------------------- //
+  FairLogger::GetLogger()->SetLogScreenLevel("INFO");
+  fair::Logger::DefineVerbosity(
+    "user1", fair::VerbositySpec::Make(fair::VerbositySpec::Info::severity, fair::VerbositySpec::Info::file_line));
+  FairLogger::GetLogger()->SetLogVerbosityLevel("user1");
+  // ---------------------------------------------------------------------------------------------------------------- //
+
+
+  // -----   CbmSetup   ----------------------------------------------------------------------------------------------------------------- //
+  auto geoSetup = CbmSetup::Instance();
+  geoSetup->LoadSetup(geoSetupTag);
+
+  // You can modify the pre-defined setup by using
+  /*
+  geoSetup->SetActive(ECbmModuleId::kMvd, kFALSE);
+  geoSetup->SetActive(ECbmModuleId::kSts, kTRUE);
+  geoSetup->SetActive(ECbmModuleId::kMuch, kFALSE);
+  geoSetup->SetActive(ECbmModuleId::kRich, kTRUE);
+  geoSetup->SetActive(ECbmModuleId::kTrd, kTRUE);
+  geoSetup->SetActive(ECbmModuleId::kTrd2d, kTRUE);
+  geoSetup->SetActive(ECbmModuleId::kTof, kTRUE);
+  geoSetup->SetActive(ECbmModuleId::kPsd, kFALSE);
+  */
+  // ---------------------------------------------------------------------------------------------------------------- //
+
+  // -----   Reconstruction run   ----------------------------------------------------------------------------------- //
+  FairRunAna* fRun = new FairRunAna();
+
+  FairFileSource* inputSource = new FairFileSource(sRecoFile);
+  inputSource->AddFriend(sUnpFile);
+  fRun->SetSource(inputSource);
+
+  FairRootFileSink* outputSink = new FairRootFileSink("temp_event_display.root");
+  fRun->SetSink(outputSink);
+
+  fRun->SetGeomFile(sGeoFile.data());
+
+  if ("" != sAlignFile) {
+    std::cout << "Applying alignment for file " << sAlignFile << std::endl;
+
+    // Define the basic structure which needs to be filled with information
+    // This structure is stored in the output file and later passed to the
+    // FairRoot framework to do the (miss)alignment
+    std::map<std::string, TGeoHMatrix>* matrices {nullptr};
+
+    // read matrices from disk
+    TFile* misalignmentMatrixRootfile = new TFile(sAlignFile.data(), "READ");
+    if (misalignmentMatrixRootfile->IsOpen()) {
+      gDirectory->GetObject("MisalignMatrices", matrices);
+      misalignmentMatrixRootfile->Close();
+    }
+    else {
+      std::cout << "Could not open file " << sAlignFile << "\n Exiting";
+      return kFALSE;
+    }
+
+    if (matrices) { fRun->AddAlignmentMatrices(*matrices); }
+    else {
+      LOG(error) << "Alignment required but no matrices found."
+                 << "\n Exiting";
+      return kFALSE;
+    }
+  }
+  // ---------------------------------------------------------------------------------------------------------------- //
+
+  // -----   Event display   ---------------------------------------------------------------------------------------- //
+  CbmTimesliceManager* fMan = new CbmTimesliceManager();
+  fMan->SetXMLConfig(sXmlGeoConfig);
+  fMan->SetDisplayMcbm();
+  // ---------------------------------------------------------------------------------------------------------------- //
+
+  fMan->Init(1, 5);  // NH display macro
+  // fMan->Init(1,4,10000);
+  // fMan->Init(1, 5, 10000);  // make STS visible by default
+  // fMan->Init(1,6,10000);  // make MVD visible by default
+
+  //-------------- NH display macro --------------------------------------------------------------------------------- //
+  cout << "customize TEveManager gEve " << gEve << endl;
+  gEve->GetDefaultGLViewer()->SetClearColor(kYellow - 10);
+  TGLViewer* v = gEve->GetDefaultGLViewer();
+  // ---------------------------------------------------------------------------------------------------------------- //
+}
diff --git a/macro/run/evt_disp_conf_cbm_sis100e.xml b/macro/run/evt_disp_conf_cbm_sis100e.xml
new file mode 100644
index 0000000000..e103722b00
--- /dev/null
+++ b/macro/run/evt_disp_conf_cbm_sis100e.xml
@@ -0,0 +1,46 @@
+<xmlconf>
+<!--
+- color is handled by the FairEventManager, converted from text to ROOT colors, ignored if "", recursive value is depth
+- transparency is handled by the FairEventManager, should be between 1 and 99, ignored if "", recursive value is depth
+  (0 and 100 theoritically, see https://root-forum.cern.ch/t/make-a-geometry-transparent/47665/9)
+- Visibility is boolean and applied to all daughters if recursive set >0
+  => To disable a full detector apart from a few leaves,
+     1) set the top node to "visibility = 0, recursive = 1"
+     2) set each intermediate node to "visibility = 0, recursive = 1"
+     3) set the nodes that should be visible to "visibility = 1, recursive = 1"
+-->
+<Detectors>
+    <detector name="cave_1" color="" transparency="" visibility="" recursive="0">
+        <detector name="platform_v22b_0" color="" transparency="" visibility="0" recursive="1"></detector>
+        <detector name="magnet_v22a_0" color="" transparency="" visibility="0" recursive="1"></detector>
+        <detector name="sts_v22c_0" color="" transparency="" visibility="1" recursive="1">
+            <detector name="sts_passive_v22c_20" color="" transparency="" visibility="0" recursive="1"></detector>
+        </detector>
+        <detector name="rich_v21a_0" color="" transparency="" visibility="1" recursive="1">
+            <detector name="rich_container_1" color="" transparency="" visibility="" recursive="1">
+                <detector name="rich_gas_1" color="" transparency="" visibility="" recursive="1">
+                    <detector name="shielding_box_1" color="" transparency="" visibility="0" recursive="1"></detector>
+                    <detector name="shielding_box_2" color="" transparency="" visibility="0" recursive="1"></detector>
+                    <detector name="sens_plane_1" color="" transparency="" visibility="0" recursive="1"></detector>
+                    <detector name="sens_plane_1" color="" transparency="" visibility="0" recursive="1"></detector>
+                    <detector name="RICH_mainframe_1_1" color="" transparency="" visibility="0" recursive="1"></detector>
+                    <detector name="RICH_mainframe_2_1" color="" transparency="" visibility="0" recursive="1"></detector>
+                </detector>
+            </detector>
+        </detector>
+        <detector name="trd_v20b_1e_0" color="" transparency="" visibility="1" recursive="1">
+            <detector name="support_trd1_1" color="" transparency="" visibility="0" recursive="1"></detector>
+        </detector>
+        <detector name="tof_v21a_1e_0" color="" transparency="" visibility="1" recursive="1">
+            <detector name="Bar_0" color="" transparency="" visibility="0" recursive="1"></detector>
+            <detector name="Bar_1" color="" transparency="" visibility="0" recursive="1"></detector>
+            <detector name="Bar_2" color="" transparency="" visibility="0" recursive="1"></detector>
+            <detector name="Bar_3" color="" transparency="" visibility="0" recursive="1"></detector>
+            <detector name="Bar_0" color="" transparency="" visibility="0" recursive="1"></detector>
+            <detector name="Bar_1" color="" transparency="" visibility="0" recursive="1"></detector>
+            <detector name="Bar_2" color="" transparency="" visibility="0" recursive="1"></detector>
+            <detector name="Bar_3" color="" transparency="" visibility="0" recursive="1"></detector>
+        </detector>
+    </detector>
+</Detectors>
+</xmlconf>
-- 
GitLab