diff --git a/src/simutrans/gui/loadsave_frame.cc b/src/simutrans/gui/loadsave_frame.cc
index bc6804e51..759196b4a 100644
--- a/src/simutrans/gui/loadsave_frame.cc
+++ b/src/simutrans/gui/loadsave_frame.cc
@@ -25,6 +25,9 @@
 #include "../network/network_socket_list.h"
 
 #include "../utils/simstring.h"
+#include "../utils/cbuffer.h"
+
+#include "../tool/simmenu.h"
 
 
 
@@ -34,18 +37,11 @@
 bool loadsave_frame_t::item_action(const char *filename)
 {
 	if(do_load) {
-		welt->switch_server( easy_server.pressed, true );
-		long start_load = dr_time();
-		if(  !welt->load(filename)  ) {
-			welt->switch_server( false, true );
-		}
-		else {
-			if (env_t::server) {
-				welt->announce_server(karte_t::SERVER_ANNOUNCE_HELLO);
-			}
-			welt->type_of_generation = karte_t::LOADED_WORLD;
-		}
-		DBG_MESSAGE( "loadsave_frame_t::item_action", "load world %li ms", dr_time() - start_load );
+		cbuffer_t param;
+		param.printf("%i,%s", easy_server.pressed, filename);
+		tool_t::simple_tool[TOOL_LOAD_WORLD]->set_default_param(param);
+		welt->set_tool(tool_t::simple_tool[TOOL_LOAD_WORLD], NULL);
+		tool_t::simple_tool[TOOL_LOAD_WORLD]->set_default_param(0);
 	}
 	else {
 		// saving a game
diff --git a/src/simutrans/gui/welt.cc b/src/simutrans/gui/welt.cc
index 7e9f7bf3c..5459fe605 100644
--- a/src/simutrans/gui/welt.cc
+++ b/src/simutrans/gui/welt.cc
@@ -36,6 +36,8 @@
 
 #include "../simcolor.h"
 
+#include "../tool/simmenu.h"
+
 #include "../display/simgraph.h"
 
 #include "../sys/simsys.h"
@@ -552,25 +554,11 @@ bool welt_gui_t::action_triggered( gui_action_creator_t *comp,value_t v)
 		translator::set_language(translator::get_language());	// reset also ingame names
 		delete sets;
 		sets = NULL;
-		if(loaded_heightfield) {
-			welt->load_heightfield(&env_t::default_settings);
-		}
-		else {
+		if (!loaded_heightfield) {
 			env_t::default_settings.heightfield = "";
-			welt->init( &env_t::default_settings, 0 );
-		}
-		destroy_all_win(true);
-		welt->step_month( env_t::default_settings.get_starting_month() );
-		welt->set_pause(false);
-		// save setting ...
-		loadsave_t file;
-		if(  file.wr_open("default.sve",loadsave_t::binary,0,"settings only",SAVEGAME_VER_NR) == loadsave_t::FILE_STATUS_OK  ) {
-			// save default setting
-			env_t::default_settings.rdwr(&file);
-			env_t::default_settings.reset_after_global_settings_reload();
-			file.close();
 		}
-		welt->type_of_generation = karte_t::NEW_WORLD;
+		// Fire init() via a tool so it runs between steps; see documentation/world-mutation-deferral.md.
+		welt->set_tool(tool_t::simple_tool[TOOL_NEW_WORLD], welt->get_active_player());
 	}
 	else if(comp==&return_menu) {
 		destroy_all_win(true);
diff --git a/src/simutrans/tool/simmenu.cc b/src/simutrans/tool/simmenu.cc
index c9938eb4f..df12904bb 100644
--- a/src/simutrans/tool/simmenu.cc
+++ b/src/simutrans/tool/simmenu.cc
@@ -167,6 +167,8 @@ const char* tool_t::id_to_string(uint16 id)
 			CASE_TO_STRING(TOOL_TOGGLE_CONTROL);
 			CASE_TO_STRING(TOOL_LOAD_SCENARIO);
 			CASE_TO_STRING(TOOL_DAY_NIGHT_TOGGLE);
+			CASE_TO_STRING(TOOL_NEW_WORLD);
+			CASE_TO_STRING(TOOL_LOAD_WORLD);
 			CASE_TO_STRING(UNUSED_TOOL_ADD_MESSAGE);
 			CASE_TO_STRING(UNUSED_WKZ_PWDHASH_TOOL);
 		}
@@ -340,6 +342,8 @@ tool_t* create_simple_tool(int toolnr)
 	case TOOL_LOAD_SCENARIO:        tool = new tool_load_scenario_t();        break;
 	case TOOL_DAY_NIGHT_TOGGLE:     tool = new tool_day_night_toggle_t();     break;
 	case TOOL_SINGLE_WAY_TOOGLE:    tool = new tool_show_single_ways_t();     break;
+	case TOOL_NEW_WORLD:            tool = new tool_new_world_t();            break;
+	case TOOL_LOAD_WORLD:           tool = new tool_load_world_t();           break;
 	case UNUSED_TOOL_ADD_MESSAGE: // fall-through - intended!!!111elf
 	case UNUSED_WKZ_PWDHASH_TOOL:
 		dbg->warning("create_simple_tool()", "Deprecated tool [%i] requested", toolnr);
diff --git a/src/simutrans/tool/simmenu.h b/src/simutrans/tool/simmenu.h
index 772d444fa..7947440ed 100644
--- a/src/simutrans/tool/simmenu.h
+++ b/src/simutrans/tool/simmenu.h
@@ -132,6 +132,8 @@ enum {
 	TOOL_LOAD_SCENARIO,
 	TOOL_DAY_NIGHT_TOGGLE,
 	TOOL_SINGLE_WAY_TOOGLE,
+	TOOL_NEW_WORLD,
+	TOOL_LOAD_WORLD,
 	SIMPLE_TOOL_COUNT,
 	SIMPLE_TOOL = 0x2000
 };
diff --git a/src/simutrans/tool/simtool.cc b/src/simutrans/tool/simtool.cc
index 7e311a8c2..f5b479b8c 100644
--- a/src/simutrans/tool/simtool.cc
+++ b/src/simutrans/tool/simtool.cc
@@ -9,6 +9,7 @@
 
 #include "../simdebug.h"
 #include "../simevent.h"
+#include "../simversion.h"
 #include "../world/simcity.h"
 #include "../simmesg.h"
 #include "../simconvoi.h"
@@ -7689,6 +7690,53 @@ bool tool_quit_t::init( player_t * )
 }
 
 
+bool tool_new_world_t::init( player_t * )
+{
+	if (env_t::default_settings.heightfield.empty()) {
+		welt->init(&env_t::default_settings, 0);
+	}
+	else {
+		welt->load_heightfield(&env_t::default_settings);
+	}
+	destroy_all_win(true);
+	welt->step_month(env_t::default_settings.get_starting_month());
+	welt->set_pause(false);
+	loadsave_t file;
+	if (file.wr_open("default.sve", loadsave_t::binary, 0, "settings only", SAVEGAME_VER_NR) == loadsave_t::FILE_STATUS_OK) {
+		env_t::default_settings.rdwr(&file);
+		env_t::default_settings.reset_after_global_settings_reload();
+		file.close();
+	}
+	welt->type_of_generation = karte_t::NEW_WORLD;
+	return false;
+}
+
+
+bool tool_load_world_t::init( player_t * )
+{
+	if (strempty(default_param)) {
+		return false;
+	}
+	int easy_server = 0;
+	const char *filename = default_param;
+	if (const char *comma = strchr(default_param, ',')) {
+		easy_server = atoi(default_param);
+		filename = comma + 1;
+	}
+	welt->switch_server(easy_server != 0, true);
+	if (!welt->load(filename)) {
+		welt->switch_server(false, true);
+	}
+	else {
+		if (env_t::server) {
+			welt->announce_server(karte_t::SERVER_ANNOUNCE_HELLO);
+		}
+		welt->type_of_generation = karte_t::LOADED_WORLD;
+	}
+	return false;
+}
+
+
 bool tool_toggle_reservation_t::is_selected() const
 {
 	if (tool_t* t = welt->get_tool(welt->get_active_player_nr())) {
diff --git a/src/simutrans/tool/simtool.h b/src/simutrans/tool/simtool.h
index 7a701a712..b9f7b17fd 100644
--- a/src/simutrans/tool/simtool.h
+++ b/src/simutrans/tool/simtool.h
@@ -1042,6 +1042,29 @@ public:
 	bool is_work_keeps_game_state() const OVERRIDE { return false; }
 };
 
+// Dispatch a new-world construction.  Carries no parameters: the tool reads
+// env_t::default_settings, which the GUI sets up before firing the tool.  An
+// empty default_settings.heightfield means procedural generation; non-empty
+// means load_heightfield.
+class tool_new_world_t : public tool_t {
+public:
+	tool_new_world_t() : tool_t(TOOL_NEW_WORLD | SIMPLE_TOOL) { flags = WFL_LOCAL | WFL_NO_CHK; }
+	char const* get_tooltip(player_t const*) const OVERRIDE { return NULL; }
+	bool init( player_t * ) OVERRIDE;
+	bool is_init_keeps_game_state() const OVERRIDE { return false; }
+	bool is_work_keeps_game_state() const OVERRIDE { return false; }
+};
+
+// Dispatch loading a savegame.  default_param is "<easy_server>,<filename>".
+class tool_load_world_t : public tool_t {
+public:
+	tool_load_world_t() : tool_t(TOOL_LOAD_WORLD | SIMPLE_TOOL) { flags = WFL_LOCAL | WFL_NO_CHK; }
+	char const* get_tooltip(player_t const*) const OVERRIDE { return NULL; }
+	bool init( player_t * ) OVERRIDE;
+	bool is_init_keeps_game_state() const OVERRIDE { return false; }
+	bool is_work_keeps_game_state() const OVERRIDE { return false; }
+};
+
 // step size by default_param
 class tool_fill_trees_t : public tool_t {
 public:
