diff --git a/overcooked_simulator/gui_2d_vis/overcooked_gui.py b/overcooked_simulator/gui_2d_vis/overcooked_gui.py
index e0a9e1aaeb121fc4469f95c060778df60fea4142..802b3b519c3eee6e75c2e546d448df86ef0010f8 100644
--- a/overcooked_simulator/gui_2d_vis/overcooked_gui.py
+++ b/overcooked_simulator/gui_2d_vis/overcooked_gui.py
@@ -59,6 +59,7 @@ class PlayerKeySet:
         pickup_key: pygame.key,
         switch_key: pygame.key,
         players: list[int],
+        joystick: int,
     ):
         """Creates a player key set which contains information about which keyboard keys control the player.
 
@@ -70,6 +71,7 @@ class PlayerKeySet:
             pickup_key: The key to pick items up or put them down.
             switch_key: The key for switching through controllable players.
             players: The player indices which this keyset can control.
+            joystick: number of joystick (later check if available)
         """
         self.move_vectors: list[list[int]] = [[-1, 0], [1, 0], [0, -1], [0, 1]]
         self.key_to_movement: dict[pygame.key, list[int]] = {
@@ -83,6 +85,7 @@ class PlayerKeySet:
         self.current_player: int = players[0] if players else 0
         self.current_idx = 0
         self.other_keyset: list[PlayerKeySet] = []
+        self.joystick = joystick
 
     def set_controlled_players(self, controlled_players: list[int]) -> None:
         self.controlled_players = controlled_players
@@ -168,6 +171,9 @@ class PyGameGUI:
         self.current_layout_idx = 0
 
     def setup_player_keys(self, number_players, number_key_sets=1, disjunct=False):
+        # First four keys are for movement. Order: Down, Up, Left, Right.
+        # 5th key is for interacting with counters.
+        # 6th key ist for picking up things or dropping them.
         if number_key_sets:
             players = list(range(number_players))
             key_set1 = PlayerKeySet(
@@ -176,6 +182,7 @@ class PyGameGUI:
                 pickup_key=pygame.K_e,
                 switch_key=pygame.K_SPACE,
                 players=players,
+                joystick=0,
             )
             key_set2 = PlayerKeySet(
                 move_keys=[pygame.K_LEFT, pygame.K_RIGHT, pygame.K_UP, pygame.K_DOWN],
@@ -183,6 +190,7 @@ class PyGameGUI:
                 pickup_key=pygame.K_o,
                 switch_key=pygame.K_p,
                 players=players,
+                joystick=1,
             )
             key_sets = [key_set1, key_set2]
 
@@ -224,6 +232,47 @@ class PyGameGUI:
                 )
                 self.send_action(action)
 
+    def handle_joy_stick_input(self, joysticks):
+        """Handles joystick inputs for movement every frame
+        Args:
+            joysticks: list of joysticks
+        """
+        # Axis 0: joy stick left: -1 = left, ~0 = center, 1 = right
+        # Axis 1: joy stick left: -1 = up, ~0 = center, 1 = down
+        # see control stuff here (at the end of the page): https://www.pygame.org/docs/ref/joystick.html
+        for key_set in self.key_sets:
+            current_player_name = str(key_set.current_player)
+            # if a joystick is connected for current player
+            if key_set.joystick in joysticks:
+                # Usually axis run in pairs, up/down for one, and left/right for the other. Triggers count as axes.
+                # You may want to take into account some tolerance to handle jitter, and
+                # joystick drift may keep the joystick from centering at 0 or using the full range of position values.
+                tolerance_threshold = 0.2
+                # axis 0 = joy stick left --> left & right
+                axis_left_right = joysticks[key_set.joystick].get_axis(0)
+                axis_up_down = joysticks[key_set.joystick].get_axis(1)
+                if (
+                    abs(axis_left_right) > tolerance_threshold
+                    or abs(axis_up_down) > tolerance_threshold
+                ):
+                    move_vec = np.zeros(2)
+                    if abs(axis_left_right) > tolerance_threshold:
+                        move_vec[0] += axis_left_right
+                    # axis 1 = joy stick right --> up & down
+                    if abs(axis_up_down) > tolerance_threshold:
+                        move_vec[1] += axis_up_down
+
+                    if np.linalg.norm(move_vec) != 0:
+                        move_vec = move_vec / np.linalg.norm(move_vec)
+
+                    action = Action(
+                        current_player_name,
+                        ActionType.MOVEMENT,
+                        move_vec,
+                        duration=self.time_delta,
+                    )
+                    self.send_action(action)
+
     def handle_key_event(self, event):
         """Handles key events for the pickup and interaction keys. Pickup is a single action,
         for interaction keydown and keyup is necessary, because the player has to be able to hold
@@ -254,6 +303,51 @@ class PyGameGUI:
                 if event.type == pygame.KEYDOWN:
                     key_set.next_player()
 
+    def handle_joy_stick_event(self, event, joysticks):
+        """Handles joy stick events for the pickup and interaction keys. Pickup is a single action,
+        for interaction buttondown and buttonup is necessary, because the player has to be able to hold
+        the button down.
+
+        Args:
+            event: Pygame event for extracting the button action.
+            joysticks: list of joysticks
+        """
+
+        for key_set in self.key_sets:
+            current_player_name = str(key_set.current_player)
+            # if a joystick is connected for current player
+            if key_set.joystick in joysticks:
+                # pickup = Button A <-> 0
+                if (
+                    joysticks[key_set.joystick].get_button(0)
+                    and event.type == pygame.JOYBUTTONDOWN
+                ):
+                    action = Action(current_player_name, ActionType.PUT, "pickup")
+                    self.send_action(action)
+
+                # interact = Button X <-> 2
+                if (
+                    joysticks[key_set.joystick].get_button(2)
+                    and event.type == pygame.JOYBUTTONDOWN
+                ):
+                    action = Action(
+                        current_player_name, ActionType.INTERACT, InterActionData.START
+                    )
+                    self.send_action(action)
+                    # stop interaction if last pressed button was X <-> 2
+                if event.button == 2 and event.type == pygame.JOYBUTTONUP:
+                    action = Action(
+                        current_player_name, ActionType.INTERACT, InterActionData.STOP
+                    )
+                    self.send_action(action)
+                # switch button Y <-> 3
+                if (
+                    joysticks[key_set.joystick].get_button(3)
+                    and not self.CONNECT_WITH_STUDY_SERVER
+                ):
+                    if event.type == pygame.JOYBUTTONDOWN:
+                        key_set.next_player()
+
     def set_window_size(self):
         if self.fullscreen:
             flags = pygame.FULLSCREEN
@@ -1294,6 +1388,7 @@ class PyGameGUI:
                         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()
@@ -1345,8 +1440,20 @@ class PyGameGUI:
 
         self.update_screen_elements()
 
+        self.reset_window_size()
+
+        self.init_ui_elements()
+        self.manage_button_visibility()
+
+        self.update_selection_elements()
+
         # Game loop
         self.running = True
+        # This dict can be left as-is, since pygame will generate a
+        # pygame.JOYDEVICEADDED event for every joystick connected
+        # at the start of the program.
+        self.joysticks = {}
+
         while self.running:
             try:
                 self.time_delta = clock.tick(self.FPS) / 1000
@@ -1357,6 +1464,23 @@ class PyGameGUI:
                         self.disconnect_websockets()
                         self.running = False
 
+                    # connect joystick
+                    if (
+                        pygame.joystick.get_count() > 0
+                        and event.type == pygame.JOYDEVICEADDED
+                    ):
+                        # This event will be generated when the program starts for every
+                        # joystick, filling up the list without needing to create them manually.
+                        joy = pygame.joystick.Joystick(event.device_index)
+                        self.joysticks[joy.get_instance_id()] = joy
+                        print(f"Joystick {joy.get_instance_id()} connected")
+
+                    # disconnect joystick
+                    if event.type == pygame.JOYDEVICEREMOVED:
+                        del self.joysticks[event.instance_id]
+                        print(f"Joystick {event.instance_id} disconnected")
+                        print("Number of joysticks:", pygame.joystick.get_count())
+
                     if event.type == pygame_gui.UI_BUTTON_PRESSED:
                         self.manage_button_event(event)
                         self.update_screen_elements()
@@ -1370,6 +1494,9 @@ class PyGameGUI:
                     ]:
                         self.handle_key_event(event)
 
+                    if event.type in [pygame.JOYBUTTONDOWN, pygame.JOYBUTTONUP] and self.menu_state == MenuStates.Game:
+                        self.handle_joy_stick_event(event, joysticks=self.joysticks)
+
                     self.manager.process_events(event)
 
                 # DRAWING