diff --git a/res/dpr-controller/Actions.json b/res/dpr-controller/Actions.json
index da76b04e26ae8d4287f8db9b6b21f64c94557ead..a7a3663a110c501656891bdd2db50e27335d6232 100644
--- a/res/dpr-controller/Actions.json
+++ b/res/dpr-controller/Actions.json
@@ -7,51 +7,51 @@
     ],
     "actions": [
         {
-            "name": "/actions/controller/in/ButtonA",
+            "name": "/actions/controller/in/button_a",
             "requirement": "mandatory",
             "type": "boolean"
         },
         {
-            "name": "/actions/controller/in/ButtonB",
+            "name": "/actions/controller/in/button_b",
             "requirement": "mandatory",
             "type": "boolean"
         },
         {
-            "name": "/actions/controller/in/ButtonX",
+            "name": "/actions/controller/in/button_x",
             "requirement": "mandatory",
             "type": "boolean"
         },
         {
-            "name": "/actions/controller/in/ButtonY",
+            "name": "/actions/controller/in/button_y",
             "requirement": "mandatory",
             "type": "boolean"
         },
         {
-            "name": "/actions/controller/in/TriggerLeft",
+            "name": "/actions/controller/in/trigger_left",
             "requirement": "mandatory",
             "type": "boolean"
         },
         {
-            "name": "/actions/controller/in/TriggerRight",
+            "name": "/actions/controller/in/trigger_right",
             "requirement": "mandatory",
             "type": "boolean"
         },
         {
-            "name": "/actions/controller/in/ThumbstickLeft",
+            "name": "/actions/controller/in/thumbstick_left",
             "requirement": "mandatory",
             "type": "vector2"
         },
         {
-            "name": "/actions/controller/in/ThumbstickRight",
+            "name": "/actions/controller/in/thumbstick_right",
             "requirement": "mandatory",
             "type": "vector2"
         },
         {
-            "name": "/actions/controller/in/TransformLeft",
+            "name": "/actions/controller/in/transform_left",
             "type": "pose"
         },
         {
-            "name": "/actions/controller/in/TransformRight",
+            "name": "/actions/controller/in/transform_right",
             "type": "pose"
         }
     ],
@@ -65,16 +65,16 @@
         {
             "language_tag": "en_us",
             "/actions/controller": "Controller Actions",
-            "/actions/controller/in/ButtonA": "Button A",
-            "/actions/controller/in/ButtonB": "Button B",
-            "/actions/controller/in/ButtonX": "Button X",
-            "/actions/controller/in/ButtonY": "Button Y",
-            "/actions/controller/in/TriggerLeft": "Trigger Left",
-            "/actions/controller/in/TriggerRight": "Trigger Right",
-            "/actions/controller/in/ThumbstickLeft": "Thumbstick Left",
-            "/actions/controller/in/ThumbstickRight": "Thumbstick Right",
-            "/actions/controller/in/TransformLeft": "Transform Left",
-            "/actions/controller/in/TransformRight": "Transform Right"
+            "/actions/controller/in/button_a": "Button A",
+            "/actions/controller/in/button_b": "Button B",
+            "/actions/controller/in/button_x": "Button X",
+            "/actions/controller/in/button_y": "Button Y",
+            "/actions/controller/in/trigger_left": "Trigger Left",
+            "/actions/controller/in/trigger_right": "Trigger Right",
+            "/actions/controller/in/thumbstick_left": "Thumbstick Left",
+            "/actions/controller/in/thumbstick_right": "Thumbstick Right",
+            "/actions/controller/in/transform_left": "Transform Left",
+            "/actions/controller/in/transform_right": "Transform Right"
         }
     ]
 }
\ No newline at end of file
diff --git a/res/dpr-controller/Default.json b/res/dpr-controller/Default.json
index d7b25209820839214853c310a75206f3dbe4f49e..a82301b45f40064e188f2ca83eaf5ac08f40d1aa 100644
--- a/res/dpr-controller/Default.json
+++ b/res/dpr-controller/Default.json
@@ -6,11 +6,11 @@
       "/actions/controller": {
          "poses": [
             {
-               "output": "/actions/controller/in/transformleft",
+               "output": "/actions/controller/in/transform_left",
                "path": "/user/hand/left/pose/tip"
             },
             {
-               "output": "/actions/controller/in/transformright",
+               "output": "/actions/controller/in/transform_right",
                "path": "/user/hand/right/pose/tip"
             }
          ],
@@ -18,7 +18,7 @@
             {
                "inputs": {
                   "click": {
-                     "output": "/actions/controller/in/triggerleft"
+                     "output": "/actions/controller/in/trigger_left"
                   }
                },
                "mode": "trigger",
@@ -27,7 +27,7 @@
             {
                "inputs": {
                   "position": {
-                     "output": "/actions/controller/in/thumbstickleft"
+                     "output": "/actions/controller/in/thumbstick_left"
                   }
                },
                "mode": "joystick",
@@ -36,7 +36,7 @@
             {
                "inputs": {
                   "click": {
-                     "output": "/actions/controller/in/buttonx"
+                     "output": "/actions/controller/in/button_x"
                   }
                },
                "mode": "button",
@@ -45,7 +45,7 @@
             {
                "inputs": {
                   "click": {
-                     "output": "/actions/controller/in/buttony"
+                     "output": "/actions/controller/in/button_y"
                   }
                },
                "mode": "button",
@@ -54,7 +54,7 @@
             {
                "inputs": {
                   "click": {
-                     "output": "/actions/controller/in/triggerright"
+                     "output": "/actions/controller/in/trigger_right"
                   }
                },
                "mode": "trigger",
@@ -63,7 +63,7 @@
             {
                "inputs": {
                   "position": {
-                     "output": "/actions/controller/in/thumbstickright"
+                     "output": "/actions/controller/in/thumbstick_right"
                   }
                },
                "mode": "joystick",
@@ -72,7 +72,7 @@
             {
                "inputs": {
                   "click": {
-                     "output": "/actions/controller/in/buttona"
+                     "output": "/actions/controller/in/button_a"
                   }
                },
                "mode": "button",
@@ -81,7 +81,7 @@
             {
                "inputs": {
                   "click": {
-                     "output": "/actions/controller/in/buttonb"
+                     "output": "/actions/controller/in/button_b"
                   }
                },
                "mode": "button",
diff --git a/src/headset/openvr_headset.cpp b/src/headset/openvr_headset.cpp
index ef96ed43c02dc8dacd40ee566c3ee2652e062aab..2d1b5739cb8434c0c652c42f8cf457b68720b793 100644
--- a/src/headset/openvr_headset.cpp
+++ b/src/headset/openvr_headset.cpp
@@ -19,7 +19,7 @@ bool OpenVRHeadset::on_setup_instance(lava::frame_config& config)
 
     if (this->system == nullptr)
     {
-        std::cout << "Can't initialize OpenVR!" << std::endl;
+        std::cout << "OpenVR: Can't initialize OpenVR!" << std::endl;
 
         return false;
     }
@@ -171,7 +171,7 @@ void OpenVRHeadset::submit_frame(VkCommandBuffer command_buffer, VkImageLayout f
     
     if (error != vr::VRCompositorError_None)
     {
-        lava::log()->error("Can't submit image to OpenVR. Maybe due to headset standby. Error code '{}'", error);
+        lava::log()->error("OpenVR: Can't submit image to OpenVR. Maybe due to headset standby. Error code '{}'", error);
     }
 }
 
@@ -285,19 +285,19 @@ bool OpenVRHeadset::create_actions()
 {
     if (vr::VRInput()->SetActionManifestPath((std::string(RESOURCE_FOLDER) + "/dpr-controller/Actions.json").c_str()) != vr::VRInputError_None)
     {
-        lava::log()->error("Can't set action manifest!");
+        lava::log()->error("OpenVR: Can't set action manifest!");
 
         return false;
     }
 
     std::vector<std::string> action_paths =
     {
-        "/actions/controller/in/ButtonA",
-        "/actions/controller/in/ButtonB",
-        "/actions/controller/in/ButtonX",
-        "/actions/controller/in/ButtonY",
-        "/actions/controller/in/TriggerLeft",
-        "/actions/controller/in/TriggerRight"
+        "/actions/controller/in/button_a",
+        "/actions/controller/in/button_b",
+        "/actions/controller/in/button_x",
+        "/actions/controller/in/button_y",
+        "/actions/controller/in/trigger_left",
+        "/actions/controller/in/trigger_right"
     };
 
     std::vector<Button> action_buttons =
@@ -314,7 +314,7 @@ bool OpenVRHeadset::create_actions()
     {
         if (vr::VRInput()->GetActionHandle(action_paths[index].c_str(), &this->controller_button_handles[action_buttons[index]]) != vr::VRInputError_None)
         {
-            lava::log()->error("Can't get handle for action: " + action_paths[index]);
+            lava::log()->error("OpenVR: Can't get handle for action: " + action_paths[index]);
 
             return false;
         }
@@ -322,18 +322,18 @@ bool OpenVRHeadset::create_actions()
 
     for (uint32_t index = 0; index < 2; index++)
     {
-        std::string hand = (index > 0) ? "Right" : "Left";
+        std::string hand = (index > 0) ? "right" : "left";
 
-        if (vr::VRInput()->GetActionHandle(("/actions/controller/in/Thumbstick" + hand).c_str(), &this->controller_thumbsticks_handles[index]) != vr::VRInputError_None)
+        if (vr::VRInput()->GetActionHandle(("/actions/controller/in/thumbstick_" + hand).c_str(), &this->controller_thumbsticks_handles[index]) != vr::VRInputError_None)
         {
-            lava::log()->error("Can't get handle for action: /action/controller/Tumbstick" + hand);
+            lava::log()->error("OpenVR: Can't get handle for action: /actions/controller/in/thumbstick_" + hand);
 
             return false;
         }
 
-        if (vr::VRInput()->GetActionHandle(("/actions/controller/in/Transform" + hand).c_str(), &this->controller_transform_handles[index]) != vr::VRInputError_None)
+        if (vr::VRInput()->GetActionHandle(("/actions/controller/in/transform_" + hand).c_str(), &this->controller_transform_handles[index]) != vr::VRInputError_None)
         {
-            lava::log()->error("Can't get handle for action: /action/controller/Transform" + hand);
+            lava::log()->error("OpenVR: Can't get handle for action: /actions/controller/in/transform_" + hand);
 
             return false;
         }
@@ -341,7 +341,7 @@ bool OpenVRHeadset::create_actions()
 
     if(vr::VRInput()->GetActionSetHandle("/actions/controller", &this->controller_action_set) != vr::VRInputError_None)
     {
-        lava::log()->error("Can't get handle for action set: /action/controller");
+        lava::log()->error("OpenVR: Can't get handle for action set: /actions/controller");
 
         return false;
     }
@@ -360,7 +360,7 @@ bool OpenVRHeadset::update_actions()
 
     if (vr::VRInput()->UpdateActionState(&active_set, sizeof(active_set), 1) != vr::VRInputError_None)
     {
-        lava::log()->error("Can't update action state!");
+        lava::log()->error("OpenVR: Can't update action state!");
 
         return false;
     }
@@ -371,7 +371,7 @@ bool OpenVRHeadset::update_actions()
 
         if (vr::VRInput()->GetDigitalActionData(this->controller_button_handles[index], &button_data, sizeof(button_data), vr::k_ulInvalidInputValueHandle) != vr::VRInputError_None)
         {
-            lava::log()->error("Can't get button action data!");
+            lava::log()->error("OpenVR: Can't get button action data!");
 
             return false;
         }
@@ -386,14 +386,14 @@ bool OpenVRHeadset::update_actions()
 
         if (vr::VRInput()->GetAnalogActionData(this->controller_thumbsticks_handles[index], &thumbstick_data, sizeof(thumbstick_data), vr::k_ulInvalidInputValueHandle) != vr::VRInputError_None)
         {
-            lava::log()->error("Can't get thumbstick action data!");
+            lava::log()->error("OpenVR: Can't get thumbstick action data!");
 
             return false;
         }
 
         if (vr::VRInput()->GetPoseActionDataForNextFrame(this->controller_transform_handles[index], vr::TrackingUniverseStanding, &transform_data, sizeof(transform_data), vr::k_ulInvalidInputValueHandle) != vr::VRInputError_None)
         {
-            lava::log()->error("Can't get transform action data!");
+            lava::log()->error("OpenVR: Can't get transform action data!");
 
             return false;
         }
@@ -422,7 +422,7 @@ bool OpenVRHeadset::update_head_pose()
 
     if (error != vr::VRCompositorError_None)
     {
-        lava::log()->error("Can't get current pose from OpenVR: Error code '{}'", error);
+        lava::log()->error("OpenVR: Can't get current pose from OpenVR: Error code '{}'", error);
 
         return false;
     }
diff --git a/src/headset/openxr_headset.cpp b/src/headset/openxr_headset.cpp
index f9f58ed5520fd317dc32f169f535fd329a12f1b6..d04cf5ba19a776a23f8cc0ed4e05a686c2bf7476 100644
--- a/src/headset/openxr_headset.cpp
+++ b/src/headset/openxr_headset.cpp
@@ -15,8 +15,9 @@ OpenXRHeadset::OpenXRHeadset()
     memset(&this->swapchain_indices, 0, sizeof(this->swapchain_indices));
 
     this->controller_button_actions.fill(XR_NULL_HANDLE);
-    this->controller_thumbsticks_actions.fill(XR_NULL_HANDLE);
+    this->controller_thumbstick_actions.fill(XR_NULL_HANDLE);
     this->controller_transform_actions.fill(XR_NULL_HANDLE);
+    this->controller_transform_space.fill(XR_NULL_HANDLE);
 
     this->controller_attached.fill(false);
     this->controller_buttons.fill(false);
@@ -31,7 +32,8 @@ bool OpenXRHeadset::on_setup_instance(lava::frame_config& config)
 
     std::vector<const char*> required_extensions =
     {
-        XR_KHR_VULKAN_ENABLE_EXTENSION_NAME
+        XR_KHR_VULKAN_ENABLE_EXTENSION_NAME,
+        XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME
     };
 
     if (!this->check_layer_support(required_layers))
@@ -299,6 +301,8 @@ bool OpenXRHeadset::on_interface()
     ImGui::DragFloat("Near Plane", &this->near_plane);
     ImGui::DragFloat("Far Plane", &this->far_plane);
 
+    ImGui::DragFloat("Movement Speed", &this->movement_speed, 1.0f, 0.0f, 1000.0);
+
     return true;
 }
 
@@ -309,13 +313,6 @@ bool OpenXRHeadset::on_update(lava::delta delta_time)
         return false;
     }
 
-    if(!this->update_actions())
-    {
-        return false;
-    }
-
-    this->update_stage(delta_time);
-
     if (this->active)
     {
         if (!this->begin_frame())
@@ -328,6 +325,16 @@ bool OpenXRHeadset::on_update(lava::delta delta_time)
             return false;
         }
 
+        if (this->focused)
+        {
+            if (!this->update_actions())
+            {
+                return false;
+            }
+
+            this->update_stage(delta_time);
+        }
+
         this->submit_count = 0;
     }
 
@@ -457,7 +464,7 @@ const char* OpenXRHeadset::get_name() const
 
 bool OpenXRHeadset::is_attached(Controller controller) const
 {
-    return false;
+    return this->controller_attached[controller];
 }
 
 bool OpenXRHeadset::create_framebuffers()
@@ -678,7 +685,7 @@ bool OpenXRHeadset::create_actions()
         thumbstick_info.subactionPaths = nullptr;
         strncpy(thumbstick_info.localizedActionName, thumbstick_name_localized.c_str(), XR_MAX_LOCALIZED_ACTION_NAME_SIZE);
 
-        if (xrCreateAction(this->action_set, &thumbstick_info, &this->controller_thumbsticks_actions[index]) != XR_SUCCESS)
+        if (xrCreateAction(this->action_set, &thumbstick_info, &this->controller_thumbstick_actions[index]) != XR_SUCCESS)
         {
             lava::log()->error("OpenXR: Can't create action: " + thumbstick_name);
 
@@ -692,7 +699,7 @@ bool OpenXRHeadset::create_actions()
         transform_info.type = XR_TYPE_ACTION_CREATE_INFO;
         transform_info.next = nullptr;
         strncpy(transform_info.actionName, transform_name.c_str(), XR_MAX_ACTION_NAME_SIZE);
-        transform_info.actionType = XR_ACTION_TYPE_VECTOR2F_INPUT;
+        transform_info.actionType = XR_ACTION_TYPE_POSE_INPUT;
         transform_info.countSubactionPaths = 0;
         transform_info.subactionPaths = nullptr;
         strncpy(transform_info.localizedActionName, transform_name_localized.c_str(), XR_MAX_LOCALIZED_ACTION_NAME_SIZE);
@@ -703,9 +710,24 @@ bool OpenXRHeadset::create_actions()
 
             return false;
         }
+
+        XrActionSpaceCreateInfo transform_space_info;
+        transform_space_info.type = XR_TYPE_ACTION_SPACE_CREATE_INFO;
+        transform_space_info.next = nullptr;
+        transform_space_info.action = this->controller_transform_actions[index];
+        transform_space_info.subactionPath = XR_NULL_PATH;
+
+        memset(&transform_space_info.poseInActionSpace, 0, sizeof(transform_space_info.poseInActionSpace));
+        transform_space_info.poseInActionSpace.orientation.w = 1.0f;
+
+        if (xrCreateActionSpace(this->session, &transform_space_info, &this->controller_transform_space[index]) != XR_SUCCESS)
+        {
+            lava::log()->error("OpenXR: Can't create action space!");
+
+            return false;
+        }
     }
 
-    std::vector<XrPath> paths;
     std::vector<std::string> path_strings =
     {
         "/user/hand/right/input/a/click",
@@ -718,14 +740,15 @@ bool OpenXRHeadset::create_actions()
         "/user/hand/right/input/thumbstick",
         "/user/hand/left/input/aim/pose",
         "/user/hand/right/input/aim/pose",
-        "/interaction_profiles/htc/vive_focus3_controller"
+        "/interaction_profiles/htc/vive_cosmos_controller"
     };
 
+    std::vector<XrPath> paths;
+    paths.resize(path_strings.size());
+
     for (uint32_t index = 0; index < path_strings.size(); index++)
     {
-        XrPath path = XR_NULL_PATH;
-
-        if (xrStringToPath(this->instance, path_strings[index].c_str(), &path) != XR_SUCCESS)
+        if (xrStringToPath(this->instance, path_strings[index].c_str(), &paths[index]) != XR_SUCCESS)
         {
             lava::log()->error("OpenXR: Can't create path for string: " + path_strings[index]);
 
@@ -735,14 +758,14 @@ bool OpenXRHeadset::create_actions()
 
     std::vector<XrAction> actions =
     {
-        this->controller_thumbsticks_actions[0],
-        this->controller_thumbsticks_actions[1],
-        this->controller_thumbsticks_actions[2],
-        this->controller_thumbsticks_actions[3],
-        this->controller_thumbsticks_actions[4],
-        this->controller_thumbsticks_actions[5],
-        this->controller_thumbsticks_actions[0],
-        this->controller_thumbsticks_actions[1],
+        this->controller_button_actions[0],
+        this->controller_button_actions[1],
+        this->controller_button_actions[2],
+        this->controller_button_actions[3],
+        this->controller_button_actions[4],
+        this->controller_button_actions[5],
+        this->controller_thumbstick_actions[0],
+        this->controller_thumbstick_actions[1],
         this->controller_transform_actions[0],
         this->controller_transform_actions[1]
     };
@@ -750,7 +773,7 @@ bool OpenXRHeadset::create_actions()
     std::vector<XrActionSuggestedBinding> bindings;
     bindings.resize(actions.size());
 
-    for (uint32_t index = 0; index < this->controller_button_actions.size(); index++)
+    for (uint32_t index = 0; index < bindings.size(); index++)
     {
         bindings[index].action = actions[index];
         bindings[index].binding = paths[index];
@@ -788,6 +811,15 @@ bool OpenXRHeadset::create_actions()
 
 void OpenXRHeadset::destroy_actions()
 {
+    for (XrSpace& space : this->controller_transform_space)
+    {
+        if (space != XR_NULL_HANDLE)
+        {
+            xrDestroySpace(space);
+            space = XR_NULL_HANDLE;
+        }
+    }
+
     for (XrAction& action : this->controller_button_actions)
     {
         if (action != XR_NULL_HANDLE)
@@ -806,7 +838,7 @@ void OpenXRHeadset::destroy_actions()
         }
     }
 
-    for (XrAction& action : this->controller_thumbsticks_actions)
+    for (XrAction& action : this->controller_thumbstick_actions)
     {
         if (action != XR_NULL_HANDLE)
         {
@@ -891,6 +923,12 @@ bool OpenXRHeadset::process_event(const XrEventDataBuffer& event)
 
                 this->active = false;
                 break;
+            case XR_SESSION_STATE_VISIBLE:
+                this->focused = false;
+                break;
+            case XR_SESSION_STATE_FOCUSED:
+                this->focused = true;
+                break;
             case XR_SESSION_STATE_EXITING:
             case XR_SESSION_STATE_LOSS_PENDING:
                 return false;
@@ -911,24 +949,160 @@ bool OpenXRHeadset::process_event(const XrEventDataBuffer& event)
 
 bool OpenXRHeadset::update_actions()
 {
+    XrActiveActionSet active_set;
+    active_set.actionSet = this->action_set;
+    active_set.subactionPath = XR_NULL_PATH;
+
+    XrActionsSyncInfo sync_info;
+    sync_info.type = XR_TYPE_ACTIONS_SYNC_INFO;
+    sync_info.next = nullptr;
+    sync_info.countActiveActionSets = 1;
+    sync_info.activeActionSets = &active_set;
+
+    if (xrSyncActions(this->session, &sync_info) != XR_SUCCESS)
+    {
+        lava::log()->error("OpenXR: Can't sync action set!");
+
+        return false;
+    }
+
     for (uint32_t index = 0; index < this->controller_button_actions.size(); index++)
     {
-        
+        XrActionStateGetInfo button_info;
+        button_info.type = XR_TYPE_ACTION_STATE_GET_INFO;
+        button_info.next = nullptr;
+        button_info.action = this->controller_button_actions[index];
+        button_info.subactionPath = XR_NULL_PATH;
+
+        XrActionStateBoolean button_state;
+        memset(&button_state, 0, sizeof(button_state));
+        button_state.type = XR_TYPE_ACTION_STATE_BOOLEAN;
+
+        if(xrGetActionStateBoolean(this->session, &button_info, &button_state) != XR_SUCCESS)
+        {
+            lava::log()->error("OpenXR: Can't get controller button state!");
+
+            return false;
+        }
+
+        this->controller_buttons[index] = button_state.isActive && button_state.currentState;
     }
 
+    for (uint32_t index = 0; index < 2; index++)
+    {
+        XrActionStateGetInfo thumbstick_info;
+        thumbstick_info.type = XR_TYPE_ACTION_STATE_GET_INFO;
+        thumbstick_info.next = nullptr;
+        thumbstick_info.action = this->controller_thumbstick_actions[index];
+        thumbstick_info.subactionPath = XR_NULL_PATH;
+
+        XrActionStateVector2f thumbstick_state;
+        memset(&thumbstick_state, 0, sizeof(thumbstick_state));
+        thumbstick_state.type = XR_TYPE_ACTION_STATE_VECTOR2F;
+
+        if (xrGetActionStateVector2f(this->session, &thumbstick_info, &thumbstick_state) != XR_SUCCESS)
+        {
+            lava::log()->error("OpenXR: Can't get controller thumbstick state!");
+
+            return false;
+        }
+
+        XrActionStateGetInfo transform_info;
+        transform_info.type = XR_TYPE_ACTION_STATE_GET_INFO;
+        transform_info.next = nullptr;
+        transform_info.action = this->controller_transform_actions[index];
+        transform_info.subactionPath = XR_NULL_PATH;
+        
+        XrActionStatePose transform_state;
+        memset(&transform_state, 0, sizeof(transform_state));
+        transform_state.type = XR_TYPE_ACTION_STATE_POSE;
+
+        if (xrGetActionStatePose(this->session, &transform_info, &transform_state) != XR_SUCCESS)
+        {
+            lava::log()->error("OpenXR: Can't get controller transform state!");
+
+            return false;
+        }
 
-    xrGetActionStateBoolean();
+        this->controller_attached[index] = transform_state.isActive;
+        this->controller_thumbsticks[index] = glm::vec2(0.0f);
+        this->controller_transforms[index] = glm::mat4(1.0f);
 
-    xrGetActionStateVector2f();
+        if (thumbstick_state.isActive)
+        {
+            this->controller_thumbsticks[index] = glm::vec2(thumbstick_state.currentState.x, thumbstick_state.currentState.y);
+        }
 
-    xrGetActionStatePose();
+        if (transform_state.isActive)
+        {
+            XrSpaceLocation transform;
+            memset(&transform, 0, sizeof(transform));
+            transform.type = XR_TYPE_SPACE_LOCATION;
+            
+            if (xrLocateSpace(this->controller_transform_space[index], this->space, this->frame_state.predictedDisplayTime, &transform) != XR_SUCCESS)
+            {
+                lava::log()->error("OpenXR: Can't locate controller!");
+
+                return false;
+            }
+
+            if ((transform.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) == 0 || (transform.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) == 0)
+            {
+                this->controller_attached[index] = false;
+
+                continue;
+            }
+
+            if ((transform.locationFlags & XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT) == 0 || (transform.locationFlags & XR_SPACE_LOCATION_POSITION_TRACKED_BIT) == 0)
+            {
+                lava::log()->warn("OpenXR: Controller not tracked correctly. Recalibration maybe neccessary!");
+
+                continue;
+            }
+
+            XrQuaternionf quaternion = transform.pose.orientation;
+            XrVector3f position = transform.pose.position;
+
+            this->controller_transforms[index] = glm::translate(glm::mat4(1.0f), glm::vec3(position.x, position.y, position.z)) * (glm::mat4)glm::quat(quaternion.w, quaternion.x, quaternion.y, quaternion.z);
+        }
+    }
 
     return true;
 }
 
 void OpenXRHeadset::update_stage(lava::delta delta_time)
 {
+    for (uint32_t index = 0; index < 2; index++)
+    {
+        glm::vec2 thumbstick = this->controller_thumbsticks[index];
+
+        glm::mat4 controller_transform = this->controller_transforms[index];
+        glm::vec3 controller_forward = controller_transform[2];
+        glm::vec3 controller_sideward = controller_transform[0];
+
+        float strength = this->movement_speed * delta_time;
 
+        if (index == CONTROLLER_LEFT && this->controller_buttons[BUTTON_TRIGGER_LEFT])
+        {
+            strength *= 2.0f;
+        }
+
+        else if (index == CONTROLLER_RIGHT && this->controller_buttons[BUTTON_TRIGGER_RIGHT])
+        {
+            strength *= 2.0f;
+        }
+
+        stage_position += controller_forward * thumbstick.y * strength;
+        stage_position -= controller_sideward * thumbstick.x * strength;
+    }
+
+    glm::mat4 stage_matrix = glm::translate(glm::mat4(1.0f), this->stage_position);
+    this->view_matrix = stage_matrix;
+
+    for (uint32_t index = 0; index < this->controller_matrices.size(); index++)
+    {
+        this->controller_matrices[index] = glm::inverse(stage_matrix) * this->controller_transforms[index];
+    }
 }
 
 bool OpenXRHeadset::begin_frame()
@@ -967,7 +1141,7 @@ bool OpenXRHeadset::update_views()
     view_info.type = XR_TYPE_VIEW_LOCATE_INFO;
     view_info.next = nullptr;
     view_info.viewConfigurationType = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO;
-    view_info.displayTime = frame_state.predictedDisplayTime;
+    view_info.displayTime = this->frame_state.predictedDisplayTime;
     view_info.space = this->space;
 
     XrViewState view_state;
@@ -1006,8 +1180,6 @@ bool OpenXRHeadset::update_views()
         return true;
     }
 
-    this->view_matrix = glm::mat4(1.0f);
-
     for (uint32_t index = 0; index < view_count; index++)
     {
         const XrView& view = this->views[index];
diff --git a/src/headset/openxr_headset.hpp b/src/headset/openxr_headset.hpp
index 19e10eaedfc669c1b833ee207204eceae5231207..745c00a3c76e38efe5a5b3f747cb89031676d760 100644
--- a/src/headset/openxr_headset.hpp
+++ b/src/headset/openxr_headset.hpp
@@ -85,7 +85,8 @@ private:
 
     XrActionSet action_set = XR_NULL_HANDLE;
     std::array<XrAction, 2> controller_transform_actions;
-    std::array<XrAction, 2> controller_thumbsticks_actions;
+    std::array<XrSpace, 2> controller_transform_space;
+    std::array<XrAction, 2> controller_thumbstick_actions;
     std::array<XrAction, BUTTON_MAX_COUNT> controller_button_actions;
 
     std::array<uint32_t, 2> swapchain_indices;
@@ -97,15 +98,16 @@ private:
     std::vector<std::string> device_extensions;
 
     bool active = false;
+    bool focused = false;
     uint32_t submit_count = 0;
 
     float near_plane = 0.1f;
     float far_plane = 1000.0f;
+    float movement_speed = 1.0f;
 
     glm::uvec2 resolution;
     glm::vec3 stage_position = glm::vec3(0.0f);
     glm::mat4 view_matrix;
-    glm::mat4 head_matrix;
     std::array<glm::mat4, 2> head_to_eye_matrices;
     std::array<glm::mat4, 2> projection_matrices;
     std::array<glm::mat4, 2> controller_matrices;