diff --git a/corsika/detail/modules/proposal/ContinuousProcess.inl b/corsika/detail/modules/proposal/ContinuousProcess.inl
index b190619b39b88f0b81ceafbb7fff4a73992d4862..36f277270a6e5a41f738589ac182b0ba69b8103f 100644
--- a/corsika/detail/modules/proposal/ContinuousProcess.inl
+++ b/corsika/detail/modules/proposal/ContinuousProcess.inl
@@ -28,11 +28,9 @@ namespace corsika::proposal {
     if (code == Code::Photon) return; // no continuous builders needed for photons
 
     // interpolate the crosssection for given media and energy cut. These may
-    // take some minutes if you have to build the tables and cannot read the
+    // take some minutes if you have to build the tables and cannot read the tables
     // from disk
-    auto const emCut = get_energy_production_threshold(
-        code); //! energy resolutions globally defined for individual particles
-    auto c = p_cross->second(media.at(comp.getHash()), emCut);
+    auto c = p_cross->second(media.at(comp.getHash()), proposal_energycutsettings[code]);
 
     // choose multiple scattering model
     static constexpr auto ms_type = PROPOSAL::MultipleScatteringType::MoliereInterpol;
diff --git a/corsika/detail/modules/proposal/InteractionModel.inl b/corsika/detail/modules/proposal/InteractionModel.inl
index b5fb6cc8f6e8a2d62580dcf8ab9da10fe413cb49..05c44ba2fb725ddcf109e1cdef71793a81d7aa74 100644
--- a/corsika/detail/modules/proposal/InteractionModel.inl
+++ b/corsika/detail/modules/proposal/InteractionModel.inl
@@ -37,12 +37,9 @@ namespace corsika::proposal {
       throw std::runtime_error("PROPOSAL could not find corresponding builder");
 
     // interpolate the crosssection for given media and energy cut. These may
-    // take some minutes if you have to build the tables and cannot read the
+    // take some minutes if you have to build the tables and cannot read the tables
     // from disk
-    auto const emCut = get_energy_production_threshold(
-        code); //! energy resolutions globally defined for individual particles
-
-    auto c = p_cross->second(media.at(comp.getHash()), emCut);
+    auto c = p_cross->second(media.at(comp.getHash()), proposal_energycutsettings[code]);
 
     // Look which interactions take place and build the corresponding
     // interaction and secondary builder. The interaction integral will
diff --git a/corsika/detail/modules/proposal/ProposalProcessBase.inl b/corsika/detail/modules/proposal/ProposalProcessBase.inl
index c7c38c10423ab6fe270c0851f7b77ca21d8ab73c..0ea18a4bf9cb6ed42a3a59bda43175110008506e 100644
--- a/corsika/detail/modules/proposal/ProposalProcessBase.inl
+++ b/corsika/detail/modules/proposal/ProposalProcessBase.inl
@@ -27,6 +27,27 @@ namespace corsika::proposal {
     return false;
   }
 
+  inline HEPEnergyType ProposalProcessBase::getOptimizedEmCut(Code code) const {
+    // get energy above which energy losses need to be considered
+    auto const production_threshold = get_energy_production_threshold(code);
+
+    HEPEnergyType lowest_table_value = 0_GeV;
+
+    // find tables for EnergyCuts closest (but still smaller than) production_threshold
+    for (auto table_energy : energycut_table_values) {
+      if (table_energy <= production_threshold && table_energy > lowest_table_value) {
+        lowest_table_value = table_energy;
+      }
+    }
+
+    if (lowest_table_value == 0_GeV) {
+      // no appropriate table available
+      return production_threshold;
+    }
+
+    return lowest_table_value;
+  };
+
   template <typename TEnvironment>
   inline ProposalProcessBase::ProposalProcessBase(TEnvironment const& _env) {
     _env.getUniverse()->walk([&](auto& vtn) {
@@ -54,6 +75,34 @@ namespace corsika::proposal {
     //! path, otherwise interpolation tables would only stored in main memory if
     //! no explicit intrpolation def is specified.
     PROPOSAL::InterpolationSettings::TABLES_PATH = corsika_data("PROPOSAL").c_str();
+
+    //! Initialize EnergyCutSettings
+    for (auto particle_code : tracked) {
+      if (particle_code == Code::Photon) {
+        // no EnergyCut for photon, only-stochastic propagation
+        continue;
+      }
+      // use optimized emcut for which tables should be available
+      auto optimized_ecut = getOptimizedEmCut(particle_code);
+      CORSIKA_LOG_INFO("PROPOSAL: Use tables with EnergyCut of {} for particle {}.",
+                       optimized_ecut, particle_code);
+      proposal_energycutsettings[particle_code] = optimized_ecut;
+    }
+
+    //! Initialize PROPOSAL tables for all media and all particles
+    for (auto medium : media) {
+      for (auto particle_code : tracked) {
+        buildTables(medium.second, particle_code,
+                    proposal_energycutsettings[particle_code]);
+      }
+    }
+  }
+
+  void ProposalProcessBase::buildTables(PROPOSAL::Medium medium, Code particle_code,
+                                        HEPEnergyType emcut) {
+    CORSIKA_LOG_DEBUG("PROPOSAL: Initialize tables for particle {} in {}", particle_code,
+                      medium.GetName());
+    cross[particle_code](medium, emcut);
   }
 
   inline size_t ProposalProcessBase::hash::operator()(const calc_key_t& p) const
diff --git a/corsika/modules/proposal/ProposalProcessBase.hpp b/corsika/modules/proposal/ProposalProcessBase.hpp
index 0f13f54b4c2b8303b75afca262c67d761d7165c2..db56b02594db7870b11a97994fb4ea02e349eb68 100644
--- a/corsika/modules/proposal/ProposalProcessBase.hpp
+++ b/corsika/modules/proposal/ProposalProcessBase.hpp
@@ -32,6 +32,14 @@ namespace corsika::proposal {
   //!
   static constexpr double v_cut = 0.01;
 
+  //!
+  //! List of EnergyCut values for which CORSIKA will, by default, create and provide
+  //! PROPOSAL tables.
+  //!
+  static constexpr std::array<HEPEnergyType, 10> energycut_table_values{
+      1000_MeV, 100_MeV, 20_MeV,   10_MeV,   3_MeV,
+      1_MeV,    0.4_MeV, 0.25_MeV, 0.15_MeV, 0.05_MeV};
+
   //!
   //! Internal map from particle codes to particle properties required for
   //! crosssections, decay and scattering algorithms. In the future the
@@ -223,6 +231,10 @@ namespace corsika::proposal {
         media; //!< maps nuclear composition from univers to media to produce
                //!< crosssections, which requires further ionization constants.
 
+    //!< save emcut for tracked particles
+    std::unordered_map<Code, corsika::units::si::HEPEnergyType>
+        proposal_energycutsettings;
+
     //!
     //! Store cut and  nuclear composition of the whole universe in media which are
     //! required for creating crosssections by proposal.
@@ -235,6 +247,12 @@ namespace corsika::proposal {
     //!
     bool canInteract(Code pcode) const;
 
+    //!
+    //! Finds the optimal EnergyCut for which PROPOSAL tables should (by default) be
+    //! available.
+    //!
+    HEPEnergyType getOptimizedEmCut(Code code) const;
+
     using calc_key_t = std::pair<std::size_t, Code>;
 
     //!
@@ -250,6 +268,11 @@ namespace corsika::proposal {
     //!
     virtual void buildCalculator(Code, NuclearComposition const&) = 0;
 
+    //!
+    //! Initialize PROPOSAL tables for given medium, code, and energy cut
+    //!
+    void buildTables(PROPOSAL::Medium, Code, HEPEnergyType);
+
     //!
     //! Searches the particle dependet calculator dependent of actuall medium composition
     //! and particle type. If no calculator is found, the corresponding new calculator is
diff --git a/examples/corsika.cpp b/examples/corsika.cpp
index 7f40521ceff1cb5c3d822a8bc5f7ff5ff2b3f077..df55471074e45dc97306e3471dc00074b97182b1 100644
--- a/examples/corsika.cpp
+++ b/examples/corsika.cpp
@@ -398,12 +398,14 @@ int main(int argc, char** argv) {
   HEPEnergyType const mucut = 1_GeV * app["--mucut"]->as<double>();
   ParticleCut<SubWriter<decltype(dEdX)>> cut(emcut, emcut, hadcut, mucut, true, dEdX);
 
-  // tell proposal that we are interested in all energy losses above the emcut
-  set_energy_production_threshold(Code::Electron, emcut);
-  set_energy_production_threshold(Code::Positron, emcut);
-  set_energy_production_threshold(Code::Photon, emcut);
-  set_energy_production_threshold(Code::MuMinus, mucut);
-  set_energy_production_threshold(Code::MuPlus, mucut);
+  // tell proposal that we are interested in all energy losses above the particle cut
+  set_energy_production_threshold(Code::Electron, std::min({emcut, hadcut, mucut}));
+  set_energy_production_threshold(Code::Positron, std::min({emcut, hadcut, mucut}));
+  set_energy_production_threshold(Code::Photon, std::min({emcut, hadcut, mucut}));
+  set_energy_production_threshold(Code::MuMinus, std::min({emcut, hadcut, mucut}));
+  set_energy_production_threshold(Code::MuPlus, std::min({emcut, hadcut, mucut}));
+  set_energy_production_threshold(Code::TauMinus, std::min({emcut, hadcut, mucut}));
+  set_energy_production_threshold(Code::TauPlus, std::min({emcut, hadcut, mucut}));
 
   // energy threshold for high energy hadronic model. Affects LE/HE switch for
   // hadron interactions and the hadronic photon model in proposal