diff --git a/source/event/include/G4EventManager.hh b/source/event/include/G4EventManager.hh
index 623861bb172f11e4f66785a0de78482ec5152670..a38eebf01d11d46aac5c4d880f6c2c3cc8cbf929 100644
--- a/source/event/include/G4EventManager.hh
+++ b/source/event/include/G4EventManager.hh
@@ -88,6 +88,10 @@ class G4EventManager
       // object has valid primary vertices/particles, they will be added to
       // the given "trackvector" input.
 
+    void StackTracks(G4TrackVector* trackVector, G4bool IDhasAlreadySet= false);
+      // Helper function to stack a vector of tracks for processing in the
+      // current event.
+
     inline const G4Event* GetConstCurrentEvent()
       { return currentEvent; }
     inline G4Event* GetNonconstCurrentEvent()
@@ -156,7 +160,6 @@ class G4EventManager
   private:
 
     void DoProcessing(G4Event* anEvent);
-    void StackTracks(G4TrackVector* trackVector, G4bool IDhasAlreadySet= false);
   
   private:
 
diff --git a/source/event/src/G4EventManager.cc b/source/event/src/G4EventManager.cc
index 6032d47956be296f870a52160066c64ff196ebc2..6cce554cb3d045c952c2ccb0fcc3a956c1783305 100644
--- a/source/event/src/G4EventManager.cc
+++ b/source/event/src/G4EventManager.cc
@@ -32,6 +32,8 @@
 #include "G4ios.hh"
 #include "G4EvManMessenger.hh"
 #include "G4Event.hh"
+#include "G4ParticleDefinition.hh"
+#include "G4VTrackingManager.hh"
 #include "G4UserEventAction.hh"
 #include "G4UserStackingAction.hh"
 #include "G4SDManager.hh"
@@ -43,6 +45,7 @@
 #include "G4Profiler.hh"
 #include "G4TiMemory.hh"
 
+#include <unordered_set>
 #include "G4GlobalFastSimulationManager.hh"
 
 G4ThreadLocal G4EventManager* G4EventManager::fpEventManager = nullptr;
@@ -107,7 +110,7 @@ void G4EventManager::DoProcessing(G4Event* anEvent)
   G4Navigator* navigator = G4TransportationManager::GetTransportationManager()
                          ->GetNavigatorForTracking();
   navigator->LocateGlobalPointAndSetup(center,0,false);
-                                                                                      
+
   G4Track* track = nullptr;
   G4TrackStatus istop = fAlive;
 
@@ -158,6 +161,7 @@ void G4EventManager::DoProcessing(G4Event* anEvent)
   }
 #endif
 
+  std::unordered_set<G4VTrackingManager *> trackingManagersToFlush;
 
  do
  { 
@@ -166,6 +170,29 @@ void G4EventManager::DoProcessing(G4Event* anEvent)
   while( (track=trackContainer->PopNextTrack(&previousTrajectory)) != nullptr )
   {                                        // Loop checking 12.28.2015 M.Asai
 
+      const G4ParticleDefinition* partDef = track->GetParticleDefinition();
+      G4VTrackingManager* particleTrackingManager = partDef->GetTrackingManager();
+
+      if (particleTrackingManager != nullptr)
+      {
+#ifdef G4VERBOSE
+        if ( verboseLevel > 1 )
+        {
+          G4cout << "Track " << track << " (trackID " << track->GetTrackID()
+                 << ", parentID " << track->GetParentID()
+                 << ") is handed over to custom TrackingManager." << G4endl;
+        }
+#endif
+
+        particleTrackingManager->HandOverOneTrack(track);
+        // The particle's tracking manager may either track immediately or
+        // defer processing until FlushEvent is called. Thus, we must neither
+        // check the track's status nor stack secondaries.
+
+        // Remember this tracking manager to later call FlushEvent.
+        trackingManagersToFlush.insert(particleTrackingManager);
+
+      } else {
 #ifdef G4VERBOSE
     if ( verboseLevel > 1 )
     {
@@ -243,10 +270,18 @@ void G4EventManager::DoProcessing(G4Event* anEvent)
         }
         delete track;
         break;
+      }
     }
   }
 
-  G4GlobalFastSimulationManager::GetGlobalFastSimulationManager()->Flush();
+    // Flush all tracking managers, which may have deferred processing until now.
+    for (G4VTrackingManager *tm : trackingManagersToFlush)
+    {
+      tm->FlushEvent();
+    }
+    trackingManagersToFlush.clear();
+
+    G4GlobalFastSimulationManager::GetGlobalFastSimulationManager()->Flush();
 
   } while (trackContainer->GetNUrgentTrack() > 0);
 
diff --git a/source/particles/management/include/G4PDefManager.hh b/source/particles/management/include/G4PDefManager.hh
index 5b1283b5fcfa146732d0d7bc5d1c29a53eddc823..f7c6914a11a2e9922524b73691580a4b0e777455 100644
--- a/source/particles/management/include/G4PDefManager.hh
+++ b/source/particles/management/include/G4PDefManager.hh
@@ -64,6 +64,7 @@
 #include "G4AutoLock.hh"
 
 class G4ProcessManager;
+class G4VTrackingManager;
 
 class G4PDefData
 {
@@ -76,6 +77,7 @@ class G4PDefData
     void initialize();
 
     G4ProcessManager* theProcessManager = nullptr;
+    G4VTrackingManager* theTrackingManager = nullptr;
 };
 
 class G4PDefManager
diff --git a/source/particles/management/include/G4ParticleDefinition.hh b/source/particles/management/include/G4ParticleDefinition.hh
index cd618658338732c7ae5fd1145003d2f845ef9289..ad2ea0b2c72c9d053c5b06067bf74d723235f2f4 100644
--- a/source/particles/management/include/G4ParticleDefinition.hh
+++ b/source/particles/management/include/G4ParticleDefinition.hh
@@ -53,6 +53,7 @@ class G4ProcessManager;
 class G4DecayTable;
 class G4ParticleTable;
 class G4ParticlePropertyTable;
+class G4VTrackingManager;
 
 using G4ParticleDefinitionSubInstanceManager = G4PDefManager;
 
@@ -162,6 +163,11 @@ class G4ParticleDefinition
       // Set/Get Process Manager
       //   !! Process Manager can be modified !!  
 
+    G4VTrackingManager* GetTrackingManager() const;
+    void SetTrackingManager(G4VTrackingManager* aTrackingManager);
+      // Set/Get Tracking Manager; nullptr means the default
+      //   !! Tracking Manager can be modified !!
+
     inline G4ParticleTable* GetParticleTable() const;
       // Get pointer to the particle table
 
diff --git a/source/particles/management/src/G4PDefManager.cc b/source/particles/management/src/G4PDefManager.cc
index 9d0ac4073690859fee03e77c7703b2d592e96561..584d43e09e741ce82035454f6e1f62944a3c80d2 100644
--- a/source/particles/management/src/G4PDefManager.cc
+++ b/source/particles/management/src/G4PDefManager.cc
@@ -39,6 +39,7 @@
 void G4PDefData::initialize()
 {
   theProcessManager = nullptr;
+  theTrackingManager = nullptr;
 }
 
 G4int& G4PDefManager::slavetotalspace()
diff --git a/source/particles/management/src/G4ParticleDefinition.cc b/source/particles/management/src/G4ParticleDefinition.cc
index d34510ad985f605627cdca271249688abf61c448..ffd5cfd6091fd24ae94d3f8a847d57076da5f456 100644
--- a/source/particles/management/src/G4ParticleDefinition.cc
+++ b/source/particles/management/src/G4ParticleDefinition.cc
@@ -53,6 +53,7 @@ G4PDefManager G4ParticleDefinition::subInstanceManager;
 // in the class G4PDefData.
 //
 #define G4MT_pmanager ((subInstanceManager.offset()[g4particleDefinitionInstanceID]).theProcessManager)
+#define G4MT_tmanager ((subInstanceManager.offset()[g4particleDefinitionInstanceID]).theTrackingManager)
 
 // --------------------------------------------------------------------
 G4ParticleDefinition::G4ParticleDefinition(
@@ -240,6 +241,13 @@ G4ProcessManager* G4ParticleDefinition::GetProcessManager() const
   return G4MT_pmanager;
 }
 
+// --------------------------------------------------------------------
+G4VTrackingManager* G4ParticleDefinition::GetTrackingManager() const
+{
+  if(g4particleDefinitionInstanceID<0) return nullptr;
+  return G4MT_tmanager;
+}
+
 // --------------------------------------------------------------------
 G4int G4ParticleDefinition::FillQuarkContents()
 {
@@ -474,3 +482,22 @@ void G4ParticleDefinition::SetProcessManager(G4ProcessManager* aProcessManager)
   }
   G4MT_pmanager = aProcessManager;
 }
+
+// --------------------------------------------------------------------
+void G4ParticleDefinition::SetTrackingManager(G4VTrackingManager* aTrackingManager)
+{
+  if(g4particleDefinitionInstanceID<0 && !isGeneralIon)
+  {
+    if(G4Threading::G4GetThreadId() >= 0)
+    {
+      G4ExceptionDescription ed;
+      ed << "TrackingManager is being set to " << theParticleName
+         << " without proper initialization of TLS pointer vector.\n"
+         << "This operation is thread-unsafe.";
+      G4Exception("G4ParticleDefintion::SetTrackingManager",
+                  "PART10118", JustWarning, ed);
+    }
+    SetParticleDefinitionID();
+  }
+  G4MT_tmanager = aTrackingManager;
+}
diff --git a/source/run/include/G4VUserPhysicsList.hh b/source/run/include/G4VUserPhysicsList.hh
index 538cc7a281368838e6c9994accbd5a9da795f329..61a8832a26556028297ddaccd23456e3c134e1a0 100644
--- a/source/run/include/G4VUserPhysicsList.hh
+++ b/source/run/include/G4VUserPhysicsList.hh
@@ -328,6 +328,10 @@ class G4VUserPhysicsList
   //    this routine is invoked from RunManager
   void RemoveProcessManager();
 
+  void RemoveTrackingManager();
+    // Remove and delete TrackingManagers for all particles in the
+    // Particle Table.
+
  public:  // with description
   // add process manager for particles created on-the-fly
   void AddProcessManager(G4ParticleDefinition* newParticle,
diff --git a/source/run/src/G4VUserPhysicsList.cc b/source/run/src/G4VUserPhysicsList.cc
index cc3e1aec59dda0ecbf03eadeef142b9287a350af..209ef809f9b92feb081b1efb94757dc82443078d 100644
--- a/source/run/src/G4VUserPhysicsList.cc
+++ b/source/run/src/G4VUserPhysicsList.cc
@@ -49,9 +49,6 @@
 //           To initialize thread specific data
 // ------------------------------------------------------------
 
-#include <fstream>
-#include <iomanip>
-
 #include "G4PhysicsListHelper.hh"
 #include "G4VUserPhysicsList.hh"
 
@@ -72,9 +69,14 @@
 #include "G4UImanager.hh"
 #include "G4UnitsTable.hh"
 #include "G4UserPhysicsListMessenger.hh"
+#include "G4VTrackingManager.hh"
 #include "G4ios.hh"
 #include "globals.hh"
 
+#include <fstream>
+#include <iomanip>
+#include <unordered_set>
+
 // This static member is thread local. For each thread, it holds the array
 // size of G4VUPLData instances.
 //
@@ -158,6 +160,7 @@ void G4VUserPhysicsList::InitializeWorker()
 void G4VUserPhysicsList::TerminateWorker()
 {
   RemoveProcessManager();
+  RemoveTrackingManager();
   delete G4MT_theMessenger;
   G4MT_theMessenger = nullptr;
 }
@@ -170,6 +173,7 @@ G4VUserPhysicsList::~G4VUserPhysicsList()
     G4MT_theMessenger = 0;
   }
   RemoveProcessManager();
+  RemoveTrackingManager();
 
   // invoke DeleteAllParticle
   theParticleTable->DeleteAllParticles();
@@ -368,6 +372,39 @@ void G4VUserPhysicsList::RemoveProcessManager()
   //  G4VUserPhysicsList::InitializeProcessManager" << G4endl;
 }
 
+// --------------------------------------------------------------------
+void G4VUserPhysicsList::RemoveTrackingManager()
+{
+  // One tracking manager may be registered for multiple particles, make sure
+  // to delete every object only once.
+  std::unordered_set<G4VTrackingManager *> trackingManagers;
+
+  // loop over all particles in G4ParticleTable
+  theParticleIterator->reset();
+  while((*theParticleIterator)())
+  {
+    G4ParticleDefinition* particle = theParticleIterator->value();
+    if (auto *trackingManager = particle->GetTrackingManager())
+    {
+#ifdef G4VERBOSE
+      if(verboseLevel > 2)
+      {
+        G4cout << "G4VUserPhysicsList::RemoveTrackingManager: ";
+        G4cout << "remove TrackingManager from ";
+        G4cout << particle->GetParticleName() << G4endl;
+      }
+#endif
+      trackingManagers.insert(trackingManager);
+      particle->SetTrackingManager(nullptr);
+    }
+  }
+
+  for (G4VTrackingManager *tm : trackingManagers)
+  {
+    delete tm;
+  }
+}
+
 ////////////////////////////////////////////////////////
 void G4VUserPhysicsList::SetCuts()
 {
@@ -656,6 +693,18 @@ void G4VUserPhysicsList::BuildPhysicsTable()
 // Change in order to share physics tables for two kind of process.
 void G4VUserPhysicsList::BuildPhysicsTable(G4ParticleDefinition* particle)
 {
+  if (auto *trackingManager = particle->GetTrackingManager())
+  {
+    if(verboseLevel > 2)
+    {
+      G4cout << "G4VUserPhysicsList::BuildPhysicsTable  "
+             << "Calculate Physics Table for " << particle->GetParticleName()
+             << " via custom TrackingManager" << G4endl;
+    }
+    trackingManager->BuildPhysicsTable(*particle);
+    return;
+  }
+
   if(!(particle->GetMasterProcessManager()))
   {
     G4cout
@@ -786,6 +835,12 @@ void G4VUserPhysicsList::BuildPhysicsTable(G4ParticleDefinition* particle)
 ///////////////////////////////////////////////////////////////
 void G4VUserPhysicsList::PreparePhysicsTable(G4ParticleDefinition* particle)
 {
+  if (auto *trackingManager = particle->GetTrackingManager())
+  {
+    trackingManager->PreparePhysicsTable(*particle);
+    return;
+  }
+
   if(!(particle->GetMasterProcessManager()))
   {
     ////    G4cout << "#### G4VUserPhysicsList::BuildPhysicsTable() -
diff --git a/source/tracking/include/G4VTrackingManager.hh b/source/tracking/include/G4VTrackingManager.hh
new file mode 100644
index 0000000000000000000000000000000000000000..823d01f03eb80709f917c7ecf7931e3d4bb242ad
--- /dev/null
+++ b/source/tracking/include/G4VTrackingManager.hh
@@ -0,0 +1,71 @@
+//
+// ********************************************************************
+// * License and Disclaimer                                           *
+// *                                                                  *
+// * The  Geant4 software  is  copyright of the Copyright Holders  of *
+// * the Geant4 Collaboration.  It is provided  under  the terms  and *
+// * conditions of the Geant4 Software License,  included in the file *
+// * LICENSE and available at  http://cern.ch/geant4/license .  These *
+// * include a list of copyright holders.                             *
+// *                                                                  *
+// * Neither the authors of this software system, nor their employing *
+// * institutes,nor the agencies providing financial support for this *
+// * work  make  any representation or  warranty, express or implied, *
+// * regarding  this  software system or assume any liability for its *
+// * use.  Please see the license in the file  LICENSE  and URL above *
+// * for the full disclaimer and the limitation of liability.         *
+// *                                                                  *
+// * This  code  implementation is the result of  the  scientific and *
+// * technical work of the GEANT4 collaboration.                      *
+// * By using,  copying,  modifying or  distributing the software (or *
+// * any work based  on the software)  you  agree  to acknowledge its *
+// * use  in  resulting  scientific  publications,  and indicate your *
+// * acceptance of all terms of the Geant4 Software license.          *
+// ********************************************************************
+//
+// G4VTrackingManager
+//
+// Class description:
+//
+// Interface class for implementing a custom tracking manager that is
+// specialized for stepping one or a small number of particle types.
+//
+// Original author: Jonas Hahnfeld, 2021
+
+#ifndef G4VTrackingManager_hh
+#define G4VTrackingManager_hh 1
+
+class G4ParticleDefinition;
+class G4Track;
+
+////////////////////////
+class G4VTrackingManager
+////////////////////////
+{
+
+  public:
+    virtual ~G4VTrackingManager() {}
+
+    virtual void BuildPhysicsTable(const G4ParticleDefinition&) {}
+      // Messaged by the Particle definition whenever cross-section tables have
+      // to be rebuilt (i.e. if new materials have been defined).
+
+    virtual void PreparePhysicsTable(const G4ParticleDefinition&) {}
+      // Messaged by the Particle definition whenever cross-section tables have
+      // to be prepared for rebuild (i.e. if new materials have been defined).
+
+    virtual void HandOverOneTrack(G4Track* aTrack) = 0;
+      // Invoking this function, a G4Track given by the argument will be
+      // handed over to this tracking manager. It may be tracked immediately
+      // or processing may be deferred to a later time, at the latest when
+      // calling FlushEvent().
+
+    virtual void FlushEvent() {}
+      // Signal that all tracks in the current event have been finished and
+      // this manager should process all tracks that may have been deferred.
+      // When called via this method, the tracking manager may stack new
+      // secondaries which will be tracked afterwards.
+
+};
+
+#endif
diff --git a/source/tracking/sources.cmake b/source/tracking/sources.cmake
index c25b1812666a8a5fecee4dea5deb756a2cc1971f..64df988056d50872fed03e1674b6d0b15e39eac1 100644
--- a/source/tracking/sources.cmake
+++ b/source/tracking/sources.cmake
@@ -26,6 +26,7 @@ geant4_define_module(NAME G4tracking
     G4UserTrackingAction.hh
     G4MultiTrackingAction.hh
     G4VSteppingVerbose.hh
+    G4VTrackingManager.hh
     G4VTrajectory.hh
     G4VTrajectoryPoint.hh
     trkgdefs.hh