diff --git a/cooperative_cuisine/pygame_2d_vis/drawing.py b/cooperative_cuisine/pygame_2d_vis/drawing.py
index 49aeb6a98be9833ee982f6ff1a2b62d79e4c65e5..08cb417b0571788f3d279b193e0d5e05c364f0d1 100644
--- a/cooperative_cuisine/pygame_2d_vis/drawing.py
+++ b/cooperative_cuisine/pygame_2d_vis/drawing.py
@@ -903,7 +903,9 @@ class Visualizer:
         res = np.stack([red, green, blue], axis=2)
         return res
 
-    def draw_recipe_image(self, screen, graph_dict, width, height, grid_size) -> None:
+    def draw_recipe_image(
+        self, screen: pygame.Surface, graph_dict, width, height, grid_size
+    ) -> None:
         screen.fill(self.config["GameWindow"]["background_color"])
         positions_dict = graph_dict["layout"]
         positions = np.array(list(positions_dict.values()))
diff --git a/cooperative_cuisine/pygame_2d_vis/gui.py b/cooperative_cuisine/pygame_2d_vis/gui.py
index cab7dc8e7c33a8521eaa125cc5bd18c033aa7036..fda8160538f3d272123165d27efcdd2b28efabcd 100644
--- a/cooperative_cuisine/pygame_2d_vis/gui.py
+++ b/cooperative_cuisine/pygame_2d_vis/gui.py
@@ -32,6 +32,7 @@ from cooperative_cuisine.utils import (
     url_and_port_arguments,
     disable_websocket_logging_arguments,
     add_list_of_manager_ids_arguments,
+    setup_logging,
 )
 
 
@@ -441,6 +442,32 @@ class PyGameGUI:
             ROOT_DIR / "pygame_2d_vis" / "gui_theme.json"
         )
 
+        ########################################################################
+        # All screens
+        ########################################################################
+
+        fullscreen_button_rect = pygame.Rect(
+            (0, 0), (self.buttons_width * 0.7, self.buttons_height)
+        )
+        fullscreen_button_rect.topright = (-self.buttons_width, 0)
+        self.fullscreen_button = pygame_gui.elements.UIButton(
+            relative_rect=fullscreen_button_rect,
+            text="Fullscreen",
+            manager=self.manager,
+            object_id="#fullscreen_button",
+            anchors={"right": "right", "top": "top"},
+        )
+
+        rect = pygame.Rect((0, 0), (self.buttons_width, self.buttons_height))
+        rect.topright = (0, 0)
+        self.quit_button = pygame_gui.elements.UIButton(
+            relative_rect=rect,
+            text="Quit Game",
+            manager=self.manager,
+            object_id="#quit_button",
+            anchors={"right": "right", "top": "top"},
+        )
+
         ########################################################################
         # Start screen
         ########################################################################
@@ -471,16 +498,6 @@ class PyGameGUI:
         new_dims = (img_width, img_height)
         self.press_a_image.set_dimensions(new_dims)
 
-        rect = pygame.Rect((0, 0), (self.buttons_width, self.buttons_height))
-        rect.topright = (0, 0)
-        self.quit_button = pygame_gui.elements.UIButton(
-            relative_rect=rect,
-            text="Quit Game",
-            manager=self.manager,
-            object_id="#quit_button",
-            anchors={"right": "right", "top": "top"},
-        )
-
         player_selection_rect = pygame.Rect(
             (0, 0),
             (
@@ -616,41 +633,70 @@ class PyGameGUI:
         # Tutorial screen
         ########################################################################
 
+        button_rect = pygame.Rect((0, 0), (220, 80))
+        button_rect.bottom = -20
+        self.continue_button = pygame_gui.elements.UIButton(
+            relative_rect=button_rect,
+            text="Continue",
+            manager=self.manager,
+            anchors={"centerx": "centerx", "bottom": "bottom"},
+        )
+
         image = pygame.image.load(
             ROOT_DIR / "pygame_2d_vis" / "tutorial_files" / "tutorial.drawio.png"
         ).convert_alpha()
         image_rect = image.get_rect()
-        image_rect.topleft = (20, self.buttons_height)
+        img_width = self.window_width * 0.7
+        img_height = img_width * (image_rect.height / image_rect.width)
+        new_dims = (img_width, img_height)
+        image = pygame.transform.scale(image, new_dims)
+        image_rect = image.get_rect()
+        # image_rect.topleft = (20, self.buttons_height)
+        # image_rect.topleft = (0, 0)
+        image_rect.left = self.window_width * 0.01
         self.tutorial_image = pygame_gui.elements.UIImage(
             image_rect,
             image,
             manager=self.manager,
-            anchors={"top": "top", "left": "left"},
+            anchors={"centery": "centery", "left": "left"},
         )
-        img_width = self.window_width * 0.8
-        img_height = img_width * (image_rect.height / image_rect.width)
-        new_dims = (img_width, img_height)
-        self.tutorial_image.set_dimensions(new_dims)
 
-        button_rect = pygame.Rect((0, 0), (220, 80))
-        button_rect.bottom = -20
-        self.continue_button = pygame_gui.elements.UIButton(
-            relative_rect=button_rect,
-            text="Continue",
+        rect = pygame.Rect(
+            (0, 0),
+            (self.window_width * 0.27, self.window_height * 0.3),
+        )
+        rect.right = 0
+        rect.top = self.window_height * 0.1
+        self.tutorial_recipe_container = pygame_gui.elements.UIPanel(
+            relative_rect=rect,
             manager=self.manager,
-            anchors={"centerx": "centerx", "bottom": "bottom"},
+            object_id="#graph_container",
+            anchors={"right": "right", "top_target": self.quit_button},
         )
 
-        fullscreen_button_rect = pygame.Rect(
-            (0, 0), (self.buttons_width * 0.7, self.buttons_height)
+        self.tutorial_recipe_graph_rect = pygame.Rect(
+            (0, 0), (self.window_width * 0.25, self.window_height * 0.2)
         )
-        fullscreen_button_rect.topright = (-self.buttons_width, 0)
-        self.fullscreen_button = pygame_gui.elements.UIButton(
-            relative_rect=fullscreen_button_rect,
-            text="Fullscreen",
+        # self.tutorial_recipe_graph_rect.bottom = 0
+        self.tutorial_graph_image = pygame_gui.elements.UIImage(
+            relative_rect=self.tutorial_recipe_graph_rect,
+            image_surface=pygame.Surface(self.tutorial_recipe_graph_rect.size),
             manager=self.manager,
-            object_id="#fullscreen_button",
-            anchors={"right": "right", "top": "top"},
+            object_id="#recipe_graph",
+            container=self.tutorial_recipe_container,
+            anchors={"centerx": "centerx", "top": "top"},
+        )
+        r = pygame.Rect((0, 0), (self.window_width * 0.25, self.buttons_height))
+        r.bottom = 0
+        text = pygame_gui.elements.UILabel(
+            text="Try making this recipe:",
+            relative_rect=r,
+            manager=self.manager,
+            container=self.tutorial_recipe_container,
+            anchors={
+                "centerx": "centerx",
+                "bottom": "bottom",
+            },
         )
 
         ########################################################################
@@ -820,6 +866,7 @@ class PyGameGUI:
             self.continue_button,
             self.quit_button,
             self.fullscreen_button,
+            self.tutorial_recipe_container,
         ]
 
         self.pregame_screen_elements = [
@@ -876,6 +923,32 @@ class PyGameGUI:
         for element in elements:
             element.show()
 
+    def update_tutorial_screen(self):
+        self.show_screen_elements(self.tutorial_screen_elements)
+
+        self.set_game_size(
+            max_height=self.window_height * 0.4,
+            max_width=self.window_width * 0.3,
+        )
+        self.game_center = (
+            self.window_width - self.game_width / 2 - (self.window_width * 0.015),
+            self.window_height - self.game_height / 2 - (self.window_height * 0.015),
+        )
+
+        width, height = self.tutorial_recipe_graph_rect.size
+        tutorial_graph_surface = pygame.Surface(
+            self.tutorial_recipe_graph_rect.size, flags=pygame.SRCALPHA
+        )
+        self.vis.draw_recipe_image(
+            tutorial_graph_surface,
+            self.level_info["recipe_graphs"][0],
+            width,
+            height,
+            grid_size=self.window_height / 16,
+        )
+        self.tutorial_graph_image.set_image(tutorial_graph_surface)
+        # self.tutorial_graph_image.set_dimensions((self.game_width, self.game_height))
+
     def update_screen_elements(self):
         match self.menu_state:
             case MenuStates.Start:
@@ -886,22 +959,7 @@ class PyGameGUI:
 
                 self.update_selection_elements()
             case MenuStates.ControllerTutorial:
-                self.show_screen_elements(self.tutorial_screen_elements)
-
-                if self.CONNECT_WITH_STUDY_SERVER:
-                    self.get_game_connection(tutorial=True)
-                else:
-                    self.create_env_on_game_server(tutorial=True)
-                self.setup_game(tutorial=True)
-
-                self.set_game_size(
-                    max_height=self.window_height * 0.3,
-                    max_width=self.window_width * 0.3,
-                )
-                self.game_center = (
-                    self.window_width - self.game_width / 2 - 20,
-                    self.window_height - self.game_height / 2 - 20,
-                )
+                self.update_tutorial_screen()
             case MenuStates.PreGame:
                 self.init_ui_elements()
                 self.show_screen_elements(self.pregame_screen_elements)
@@ -1280,17 +1338,31 @@ class PyGameGUI:
             (self.scroll_width * 0.95, container_height)
         )
 
-    def get_game_connection(self, tutorial):
-        if self.menu_state == MenuStates.ControllerTutorial:
+    def setup_tutorial(self):
+        answer = requests.post(
+            f"{self.request_url}/connect_to_tutorial/{self.participant_id}"
+        )
+        if answer.status_code == 200:
             answer = requests.post(
-                f"{self.request_url}/connect_to_tutorial/{self.participant_id}"
+                f"{self.request_url}/get_game_connection/{self.participant_id}"
             )
             if answer.status_code == 200:
-                self.player_info = answer.json()
+                answer_json = answer.json()
+                self.player_info = answer_json["player_info"]["0"]
+                print("TUTORIAL PLAYER INFO", self.player_info)
+                self.level_info = answer_json["level_info"]
                 self.player_info = {self.player_info["player_id"]: self.player_info}
             else:
-                self.menu_state = MenuStates.Start
                 log.warning("Could not connect to tutorial.")
+        else:
+            self.menu_state = MenuStates.Start
+            log.warning("Could not create tutorial.")
+
+    def get_game_connection(self):
+        if self.menu_state == MenuStates.ControllerTutorial:
+            self.setup_tutorial()
+            self.key_sets = self.setup_player_keys(["0"], 1, False)
+            self.vis.create_player_colors(1)
         else:
             answer = requests.post(
                 f"{self.request_url}/get_game_connection/{self.participant_id}"
@@ -1298,16 +1370,14 @@ class PyGameGUI:
             if answer.status_code == 200:
                 answer_json = answer.json()
                 self.player_info = answer_json["player_info"]
+                print("GAME PLAYER INFO", self.player_info)
+
                 self.level_info = answer_json["level_info"]
                 self.last_level = self.level_info["last_level"]
             else:
                 log.warning("COULD NOT GET GAME CONNECTION")
                 self.menu_state = MenuStates.Start
 
-        if tutorial:
-            self.key_sets = self.setup_player_keys(["0"], 1, False)
-            self.vis.create_player_colors(1)
-        else:
             self.number_players = (
                 self.number_humans_to_be_added + self.number_bots_to_be_added
             )
@@ -1385,7 +1455,7 @@ class PyGameGUI:
             if p == 0:
                 self.state_player_id = player_id
 
-    def setup_game(self, tutorial=False):
+    def setup_game(self):
         self.connect_websockets()
 
         state = self.request_state()
@@ -1537,10 +1607,9 @@ class PyGameGUI:
         answer = requests.post(
             f"{self.request_url}/start_study/{self.participant_id}/{self.number_humans_to_be_added}"
         )
-        print("START STUDY ANSWER", answer)
         if answer.status_code == 200:
             self.last_level = False
-            self.get_game_connection(tutorial=False)
+            self.get_game_connection()
         else:
             self.menu_state = MenuStates.Start
             print(
@@ -1555,10 +1624,10 @@ class PyGameGUI:
     def button_continue_postgame_pressed(self):
         if self.CONNECT_WITH_STUDY_SERVER:
             if not self.last_level:
-                self.get_game_connection(tutorial=False)
+                self.get_game_connection()
         else:
             self.current_layout_idx += 1
-            self.create_env_on_game_server(tutorial=False)
+            self.create_env_on_game_server()
             if self.current_layout_idx == len(self.layout_file_paths) - 1:
                 self.last_level = True
             else:
@@ -1566,8 +1635,8 @@ class PyGameGUI:
 
         self.menu_state = MenuStates.PreGame
 
-    def manage_button_event(self, event):
-        if event.ui_element == self.quit_button:
+    def manage_button_event(self, button: pygame_gui.core.UIElement):
+        if button == self.quit_button:
             if self.fullscreen:
                 self.fullscreen_button_press()
             self.running = False
@@ -1577,7 +1646,7 @@ class PyGameGUI:
             log.debug("Pressed quit button")
             return
 
-        elif event.ui_element == self.fullscreen_button:
+        elif button == self.fullscreen_button:
             self.fullscreen_button_press()
             log.debug("Pressed fullscreen button")
             return
@@ -1586,7 +1655,7 @@ class PyGameGUI:
         match self.menu_state:
             ############################################
             case MenuStates.Start:
-                match event.ui_element:
+                match button:
                     case self.start_button:
                         if not (
                             self.number_humans_to_be_added
@@ -1595,6 +1664,11 @@ class PyGameGUI:
                             pass
                         else:
                             self.menu_state = MenuStates.ControllerTutorial
+                            if self.CONNECT_WITH_STUDY_SERVER:
+                                self.get_game_connection()
+                            else:
+                                self.create_env_on_game_server(tutorial=True)
+                            self.setup_game()
 
                     case self.add_human_player_button:
                         self.number_humans_to_be_added += 1
@@ -1622,14 +1696,19 @@ class PyGameGUI:
             ############################################
 
             case MenuStates.ControllerTutorial:
-                match event.ui_element:
+                match button:
                     case self.continue_button:
                         self.exit_tutorial()
 
+                        if self.CONNECT_WITH_STUDY_SERVER:
+                            self.start_study()
+                        else:
+                            self.create_env_on_game_server(tutorial=False)
+
             ############################################
 
             case MenuStates.PreGame:
-                match event.ui_element:
+                match button:
                     case self.continue_button:
                         self.setup_game()
 
@@ -1638,22 +1717,8 @@ class PyGameGUI:
 
             ############################################
 
-            case MenuStates.Game:
-                pass
-                # match event.ui_element:
-                #     case self.finished_button:
-                #         self.menu_state = MenuStates.PostGame
-                #         self.disconnect_websockets()
-                #         self.finished_button_press()
-                #         self.handle_joy_stick_input(joysticks=self.joysticks)
-                #
-                #         if self.CONNECT_WITH_STUDY_SERVER:
-                #             self.send_level_done()
-
-            ############################################
-
             case MenuStates.PostGame:
-                match event.ui_element:
+                match button:
                     case self.next_game_button:
                         self.button_continue_postgame_pressed()
 
@@ -1662,11 +1727,6 @@ class PyGameGUI:
 
             ############################################
 
-            case MenuStates.End:
-                match event.ui_element:
-                    case other:
-                        pass
-
     def start_pygame(self):
         """Starts pygame and the gui loop. Each frame the game state is visualized and keyboard inputs are read."""
         log.debug(f"Starting pygame gui at {self.FPS} fps")
@@ -1733,29 +1793,20 @@ class PyGameGUI:
                     ):
                         match self.menu_state:
                             case MenuStates.Start:
-                                self.menu_state = MenuStates.ControllerTutorial
-                                self.update_screen_elements()
+                                self.manage_button_event(self.start_button)
                             case MenuStates.ControllerTutorial:
-                                self.exit_tutorial()
-                                self.update_screen_elements()
+                                self.manage_button_event(self.continue_button)
                             case MenuStates.PreGame:
-                                self.setup_game()
-                                self.set_game_size()
-                                self.menu_state = MenuStates.Game
-                                self.update_screen_elements()
+                                self.manage_button_event(self.continue_button)
                             case MenuStates.PostGame:
                                 if self.last_level:
-                                    self.menu_state = MenuStates.End
+                                    self.manage_button_event(self.next_game_button)
                                 else:
-                                    self.button_continue_postgame_pressed()
-                                self.update_screen_elements()
-
-                    # if event.type == pygame.MOUSEWHEEL:
-                    #     print(event.x, event.y)
-                    #     self.scroll_space.process_event(event)
+                                    self.manage_button_event(self.finish_study_button)
+                        self.update_screen_elements()
 
                     if event.type == pygame_gui.UI_BUTTON_PRESSED:
-                        self.manage_button_event(event)
+                        self.manage_button_event(event.ui_element)
                         self.update_screen_elements()
 
                     if event.type in [
@@ -1794,16 +1845,12 @@ class PyGameGUI:
         sys.exit()
 
     def exit_tutorial(self):
+        self.disconnect_websockets()
         self.menu_state = MenuStates.PreGame
         if self.CONNECT_WITH_STUDY_SERVER:
             self.send_tutorial_finished()
-            self.start_study()
-
         else:
             self.stop_game_on_server("tutorial_finished")
-            self.create_env_on_game_server(tutorial=False)
-
-        self.disconnect_websockets()
 
 
 def main(
@@ -1815,7 +1862,7 @@ def main(
     CONNECT_WITH_STUDY_SERVER=False,
     USE_AAAMBOS_AGENT=False,
 ):
-    # setup_logging()
+    setup_logging()
     gui = PyGameGUI(
         study_host=study_url,
         study_port=study_port,
diff --git a/cooperative_cuisine/pygame_2d_vis/tutorial.png b/cooperative_cuisine/pygame_2d_vis/tutorial.png
deleted file mode 100644
index 151f8e85b9a76c30df20d334be4deffd8d4e3d6a..0000000000000000000000000000000000000000
Binary files a/cooperative_cuisine/pygame_2d_vis/tutorial.png and /dev/null differ
diff --git a/cooperative_cuisine/pygame_2d_vis/tutorial_files/tutorial.drawio.png b/cooperative_cuisine/pygame_2d_vis/tutorial_files/tutorial.drawio.png
index 8824cce8164089cc251217e21263c75fcd552254..f4bb6e024e718f003a519e3c7d2e7fc79c65dd8f 100644
Binary files a/cooperative_cuisine/pygame_2d_vis/tutorial_files/tutorial.drawio.png and b/cooperative_cuisine/pygame_2d_vis/tutorial_files/tutorial.drawio.png differ
diff --git a/cooperative_cuisine/state_representation.py b/cooperative_cuisine/state_representation.py
index a8377e4562356859dfead8f1bca151c9ee2afb49..130b2492cdebb7a6fc7917cef8aa0bb3de0ff917 100644
--- a/cooperative_cuisine/state_representation.py
+++ b/cooperative_cuisine/state_representation.py
@@ -61,14 +61,14 @@ class PlayerState(TypedDict):
     current_nearest_counter_id: str | None
 
 
-class KitchenInfo(BaseModel):
+class KitchenInfo(TypedDict):
     """Basic information of the kitchen."""
 
     width: float
     height: float
 
 
-class ViewRestriction(BaseModel):
+class ViewRestriction(TypedDict):
     direction: list[float]
     position: list[float]
     angle: int  # degrees
diff --git a/cooperative_cuisine/study_server.py b/cooperative_cuisine/study_server.py
index c184311154e9ef5eae42740342271120c017ff1e..57ae90e8c2b40f9dc40abcff5536c75f04a81f48 100644
--- a/cooperative_cuisine/study_server.py
+++ b/cooperative_cuisine/study_server.py
@@ -26,7 +26,6 @@ import requests
 import uvicorn
 import yaml
 from fastapi import FastAPI, HTTPException
-from fastapi.responses import JSONResponse
 from pydantic import BaseModel
 
 from cooperative_cuisine import ROOT_DIR
@@ -99,7 +98,7 @@ class Study:
         """List of level configs for each of the levels which the study runs through."""
         self.current_level_idx: int = 0
         """Counter of which level is currently run in the config."""
-        self.participant_id_to_player_info: dict[str, PlayerInfo] = {}
+        self.participant_id_to_player_info: dict[str, dict[str, PlayerInfo]] = {}
         """A dictionary which maps participants to player infos."""
         self.num_connected_players: int = 0
         """Number of currently connected players."""
@@ -262,7 +261,7 @@ class Study:
 
     def get_connection(
         self, participant_id: str
-    ) -> Tuple[PlayerInfo | None, LevelInfo | None]:
+    ) -> Tuple[dict[str, PlayerInfo] | None, LevelInfo | None]:
         """Get the assigned connections to the game server for a participant.
 
         Args:
@@ -371,9 +370,7 @@ class StudyManager:
         self.participant_id_to_study_map: dict[str, Study] = {}
         """Dict which maps participants to studies."""
 
-        self.running_tutorials: dict[
-            str, Tuple[int, dict[str, PlayerInfo], list[str]]
-        ] = {}
+        self.running_tutorials: dict[str, CreateEnvResult] = {}
         """Dict which saves currently running tutorial envs, as these do not need advanced player management."""
 
         self.study_config_path = ROOT_DIR / "configs" / "study" / "study_config.yml"
@@ -428,7 +425,7 @@ class StudyManager:
 
     def get_participant_game_connection(
         self, participant_id: str
-    ) -> Tuple[PlayerInfo, LevelInfo]:
+    ) -> Tuple[dict[str, PlayerInfo], LevelInfo]:
         """Get the assigned connections to the game server for a participant.
 
         Args:
@@ -438,6 +435,16 @@ class StudyManager:
             information if the level is the last one and which recipes are possible in the level.
         Raises: HTTPException(409) if the player not registered in any study.
         """
+        if participant_id in self.running_tutorials.keys():
+            tutorial_env = self.running_tutorials[participant_id]
+            level_info = LevelInfo(
+                name="Tutorial",
+                last_level=False,
+                recipe_graphs=tutorial_env["recipe_graphs"],
+            )
+            player_info = tutorial_env["player_info"]
+            return player_info, level_info
+
         if participant_id in self.participant_id_to_study_map.keys():
             assigned_study = self.participant_id_to_study_map[participant_id]
             player_info, level_info = assigned_study.get_connection(participant_id)
@@ -476,6 +483,63 @@ class StudyManager:
         # TODO validate study_config?
         self.study_config_path = study_config_path
 
+    def start_tutorial(self, participant_id: str):
+        environment_config_path = ROOT_DIR / "configs" / "tutorial_env_config.yaml"
+        layout_path = ROOT_DIR / "configs" / "layouts" / "tutorial.layout"
+        item_info_path = ROOT_DIR / "configs" / "item_info.yaml"
+
+        with open(item_info_path, "r") as file:
+            item_info = file.read()
+        with open(layout_path, "r") as file:
+            layout = file.read()
+        with open(environment_config_path, "r") as file:
+            environment_config = file.read()
+
+        creation_json = CreateEnvironmentConfig(
+            manager_id=study_manager.server_manager_id,
+            number_players=1,
+            environment_settings={"all_player_can_pause_game": False},
+            item_info_config=item_info,
+            environment_config=environment_config,
+            layout_config=layout,
+            seed=1234567890,
+        ).model_dump(mode="json")
+        # todo async
+        env_info = requests.post(
+            study_manager.game_server_url + "/manage/create_env/", json=creation_json
+        )
+        match env_info.status_code:
+            case 200:
+                env_info = env_info.json()
+                print("CREATE TUTORIAL:", env_info)
+                study_manager.running_tutorials[participant_id] = env_info
+            case 403:
+                raise HTTPException(
+                    status_code=403,
+                    detail=f"Forbidden Request: {env_info.json()['detail']}",
+                )
+            case 500:
+                raise HTTPException(
+                    status_code=500,
+                    detail=f"Game server crashed: {env_info.json()['detail']}",
+                )
+
+    def end_tutorial(self, participant_id: str):
+        env = study_manager.running_tutorials[participant_id]
+        answer = requests.post(
+            f"{study_manager.game_server_url}/manage/stop_env/",
+            json={
+                "manager_id": study_manager.server_manager_id,
+                "env_id": env["env_id"],
+                "reason": "Finished tutorial",
+            },
+        )
+        if answer.status_code != 200:
+            raise HTTPException(
+                status_code=503, detail="Could not disconnect from tutorial"
+            )
+        del study_manager.running_tutorials[participant_id]
+
 
 study_manager = StudyManager()
 
@@ -523,55 +587,16 @@ async def get_game_connection(
 
 
 @app.post("/connect_to_tutorial/{participant_id}")
-async def connect_to_tutorial(participant_id: str) -> JSONResponse:
+async def connect_to_tutorial(participant_id: str):
     """Request of a participant to start a tutorial env and connect to it.
 
     Args:
         participant_id: ID of the requesting participant.
-    Returns: Player info which contains game server connection information.
     Raises:
         HTTPException(403) if the game server returns 403
         HTTPException(500) if the game server returns 500
     """
-    environment_config_path = ROOT_DIR / "configs" / "tutorial_env_config.yaml"
-    layout_path = ROOT_DIR / "configs" / "layouts" / "tutorial.layout"
-    item_info_path = ROOT_DIR / "configs" / "item_info.yaml"
-
-    with open(item_info_path, "r") as file:
-        item_info = file.read()
-    with open(layout_path, "r") as file:
-        layout = file.read()
-    with open(environment_config_path, "r") as file:
-        environment_config = file.read()
-
-    creation_json = CreateEnvironmentConfig(
-        manager_id=study_manager.server_manager_id,
-        number_players=1,
-        environment_settings={"all_player_can_pause_game": False},
-        item_info_config=item_info,
-        environment_config=environment_config,
-        layout_config=layout,
-        seed=1234567890,
-    ).model_dump(mode="json")
-    # todo async
-    env_info = requests.post(
-        study_manager.game_server_url + "/manage/create_env/", json=creation_json
-    )
-    match env_info.status_code:
-        case 200:
-            env_info = env_info.json()
-            study_manager.running_tutorials[participant_id] = env_info
-            return JSONResponse(content=env_info["player_info"]["0"])
-        case 403:
-            raise HTTPException(
-                status_code=403,
-                detail=f"Forbidden Request: {env_info.json()['detail']}",
-            )
-        case 500:
-            raise HTTPException(
-                status_code=500,
-                detail=f"Game server crashed: {env_info.json()['detail']}",
-            )
+    study_manager.start_tutorial(participant_id)
 
 
 @app.post("/disconnect_from_tutorial/{participant_id}")
@@ -583,18 +608,7 @@ async def disconnect_from_tutorial(participant_id: str):
 
     Raises: HTTPException(503) if the game server returns some error.
     """
-    answer = requests.post(
-        f"{study_manager.game_server_url}/manage/stop_env/",
-        json={
-            "manager_id": study_manager.server_manager_id,
-            "env_id": study_manager.running_tutorials[participant_id]["env_id"],
-            "reason": "Finished tutorial",
-        },
-    )
-    if answer.status_code != 200:
-        raise HTTPException(
-            status_code=503, detail="Could not disconnect from tutorial"
-        )
+    study_manager.end_tutorial(participant_id)
 
 
 def main(study_host, study_port, game_host, game_port, manager_ids, study_config_path):