diff --git a/src/headset/openvr_headset.hpp b/src/headset/openvr_headset.hpp
index f6fdd1168afd7937d33fde6e44ef6dc069b7aac2..b54930212ceb27e30cec0bc94fb94e2af7201887 100644
--- a/src/headset/openvr_headset.hpp
+++ b/src/headset/openvr_headset.hpp
@@ -59,19 +59,6 @@ private:
 private:
     vr::IVRSystem* system = nullptr;
 
-    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;
-
-    std::array<bool, 2> controller_attached;
-    std::array<glm::mat4, 2> controller_transforms;
-    std::array<glm::vec2, 2> controller_thumbsticks;
-    std::array<bool, BUTTON_MAX_COUNT> controller_buttons;
-
     vr::TrackedDevicePose_t head_pose;
     vr::VRActionSetHandle_t controller_action_set;
     std::array<vr::VRActionHandle_t, 2> controller_transform_handles;
@@ -81,10 +68,23 @@ private:
     std::array<lava::image::ptr, 2> framebuffers;
     lava::image::ptr framebuffer_array;
 
+    std::vector<std::string> instance_extensions;
+    std::vector<std::string> device_extensions;
+
     float near_plane = 0.1f;
     float far_plane = 1000.0f;
     float movement_speed = 1.0f;
 
-    std::vector<std::string> instance_extensions;
-    std::vector<std::string> device_extensions;
+    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;
+
+    std::array<bool, 2> controller_attached;
+    std::array<glm::mat4, 2> controller_transforms;
+    std::array<glm::vec2, 2> controller_thumbsticks;
+    std::array<bool, BUTTON_MAX_COUNT> controller_buttons;
 };
\ No newline at end of file
diff --git a/src/headset/openxr_headset.cpp b/src/headset/openxr_headset.cpp
index 0b94906b457a395def2490096f4b8da7d903d6ed..f9f58ed5520fd317dc32f169f535fd329a12f1b6 100644
--- a/src/headset/openxr_headset.cpp
+++ b/src/headset/openxr_headset.cpp
@@ -13,6 +13,13 @@ PFN_xrGetVulkanGraphicsDeviceKHR xrGetVulkanGraphicsDeviceKHR = nullptr;
 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_transform_actions.fill(XR_NULL_HANDLE);
+
+    this->controller_attached.fill(false);
+    this->controller_buttons.fill(false);
 }
 
 bool OpenXRHeadset::on_setup_instance(lava::frame_config& config)
@@ -264,11 +271,17 @@ bool OpenXRHeadset::on_create()
         return false;
     }
 
+    if (!this->create_actions())
+    {
+        return false;
+    }
+
     return true;
 }
 
 void OpenXRHeadset::on_destroy()
 {
+    this->destroy_actions();
     this->destroy_framebuffers();
     this->destroy_swapchains();
 
@@ -296,6 +309,13 @@ 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())
@@ -589,6 +609,219 @@ void OpenXRHeadset::destroy_swapchains()
     this->swapchains.clear();
 }
 
+bool OpenXRHeadset::create_actions()
+{
+    XrActionSetCreateInfo action_set_info;
+    action_set_info.type = XR_TYPE_ACTION_SET_CREATE_INFO;
+    action_set_info.next = nullptr;
+    strncpy(action_set_info.actionSetName, "controller_actions", XR_MAX_ACTION_SET_NAME_SIZE);
+    strncpy(action_set_info.localizedActionSetName, "Controller Actions", XR_MAX_LOCALIZED_ACTION_SET_NAME_SIZE);
+    action_set_info.priority = 0;
+
+    if (xrCreateActionSet(this->instance, &action_set_info, &this->action_set) != XR_SUCCESS)
+    {
+        lava::log()->error("OpenXR: Can't create action set!");
+
+        return false;
+    }
+
+    std::vector<std::string> action_names = 
+    {
+        "button_a",
+        "button_b",
+        "button_x",
+        "button_y",
+        "trigger_left",
+        "trigger_right",
+    };
+
+    std::vector<std::string> action_localized_names =
+    {
+        "Button A",
+        "Button B",
+        "Button X",
+        "Button Y",
+        "Trigger Left",
+        "Trigger Right",
+    };
+
+    for (uint32_t index = 0; index < action_names.size(); index++)
+    {
+        XrActionCreateInfo button_info;
+        button_info.type = XR_TYPE_ACTION_CREATE_INFO;
+        button_info.next = nullptr;
+        strncpy(button_info.actionName, action_names[index].c_str(), XR_MAX_ACTION_NAME_SIZE);
+        button_info.actionType = XR_ACTION_TYPE_BOOLEAN_INPUT;
+        button_info.countSubactionPaths = 0;
+        button_info.subactionPaths = nullptr;
+        strncpy(button_info.localizedActionName, action_localized_names[index].c_str(), XR_MAX_LOCALIZED_ACTION_NAME_SIZE);
+       
+        if (xrCreateAction(this->action_set, &button_info, &this->controller_button_actions[index]) != XR_SUCCESS)
+        {
+            lava::log()->error("OpenXR: Can't create action: " + action_names[index]);
+
+            return false;
+        }
+    }
+
+    for (uint32_t index = 0; index < 2; index++)
+    {
+        std::string thumbstick_name = (index > 0) ? "thumbstick_right" : "thumbstick_left";
+        std::string thumbstick_name_localized = (index > 0) ? "Thumbstick Right" : "Thumbstick Left";
+
+        XrActionCreateInfo thumbstick_info;
+        thumbstick_info.type = XR_TYPE_ACTION_CREATE_INFO;
+        thumbstick_info.next = nullptr;
+        strncpy(thumbstick_info.actionName, thumbstick_name.c_str(), XR_MAX_ACTION_NAME_SIZE);
+        thumbstick_info.actionType = XR_ACTION_TYPE_VECTOR2F_INPUT;
+        thumbstick_info.countSubactionPaths = 0;
+        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)
+        {
+            lava::log()->error("OpenXR: Can't create action: " + thumbstick_name);
+
+            return false;
+        }
+
+        std::string transform_name = (index > 0) ? "transform_right" : "transform_left";
+        std::string transform_name_localized = (index > 0) ? "Transform Right" : "Transform Left";
+
+        XrActionCreateInfo transform_info;
+        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.countSubactionPaths = 0;
+        transform_info.subactionPaths = nullptr;
+        strncpy(transform_info.localizedActionName, transform_name_localized.c_str(), XR_MAX_LOCALIZED_ACTION_NAME_SIZE);
+
+        if (xrCreateAction(this->action_set, &transform_info, &this->controller_transform_actions[index]) != XR_SUCCESS)
+        {
+            lava::log()->error("OpenXR: Can't create action: " + transform_name);
+
+            return false;
+        }
+    }
+
+    std::vector<XrPath> paths;
+    std::vector<std::string> path_strings =
+    {
+        "/user/hand/right/input/a/click",
+        "/user/hand/right/input/b/click",
+        "/user/hand/left/input/x/click",
+        "/user/hand/left/input/y/click",
+        "/user/hand/left/input/trigger/click",
+        "/user/hand/right/input/trigger/click",
+        "/user/hand/left/input/thumbstick",
+        "/user/hand/right/input/thumbstick",
+        "/user/hand/left/input/aim/pose",
+        "/user/hand/right/input/aim/pose",
+        "/interaction_profiles/htc/vive_focus3_controller"
+    };
+
+    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)
+        {
+            lava::log()->error("OpenXR: Can't create path for string: " + path_strings[index]);
+
+            return false;
+        }
+    }
+
+    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_transform_actions[0],
+        this->controller_transform_actions[1]
+    };
+
+    std::vector<XrActionSuggestedBinding> bindings;
+    bindings.resize(actions.size());
+
+    for (uint32_t index = 0; index < this->controller_button_actions.size(); index++)
+    {
+        bindings[index].action = actions[index];
+        bindings[index].binding = paths[index];
+    }
+
+    XrInteractionProfileSuggestedBinding suggested_bindings;
+    suggested_bindings.type = XR_TYPE_INTERACTION_PROFILE_SUGGESTED_BINDING;
+    suggested_bindings.next = nullptr;
+    suggested_bindings.interactionProfile = paths.back();
+    suggested_bindings.countSuggestedBindings = bindings.size();
+    suggested_bindings.suggestedBindings = bindings.data();
+    
+    if (xrSuggestInteractionProfileBindings(this->instance, &suggested_bindings) != XR_SUCCESS)
+    {
+        lava::log()->error("OpenXR: Can't set suggested bindings");
+
+        return false;
+    }
+
+    XrSessionActionSetsAttachInfo attach_info;
+    attach_info.type = XR_TYPE_SESSION_ACTION_SETS_ATTACH_INFO;
+    attach_info.next = nullptr;
+    attach_info.countActionSets = 1;
+    attach_info.actionSets = &this->action_set;
+
+    if (xrAttachSessionActionSets(this->session, &attach_info) != XR_SUCCESS)
+    {
+        lava::log()->error("OpenXR: Can't attach action set to session!");
+
+        return false;
+    }
+
+    return true;
+}
+
+void OpenXRHeadset::destroy_actions()
+{
+    for (XrAction& action : this->controller_button_actions)
+    {
+        if (action != XR_NULL_HANDLE)
+        {
+            xrDestroyAction(action);
+            action = XR_NULL_HANDLE;
+        }
+    }
+
+    for (XrAction& action : this->controller_transform_actions)
+    {
+        if (action != XR_NULL_HANDLE)
+        {
+            xrDestroyAction(action);
+            action = XR_NULL_HANDLE;
+        }
+    }
+
+    for (XrAction& action : this->controller_thumbsticks_actions)
+    {
+        if (action != XR_NULL_HANDLE)
+        {
+            xrDestroyAction(action);
+            action = XR_NULL_HANDLE;
+        }
+    }
+
+    if (this->action_set != XR_NULL_HANDLE)
+    {
+        xrDestroyActionSet(this->action_set);
+        this->action_set = XR_NULL_HANDLE;
+    }
+}
+
 bool OpenXRHeadset::poll_events()
 {
     while (true)
@@ -676,6 +909,28 @@ bool OpenXRHeadset::process_event(const XrEventDataBuffer& event)
     return true;
 }
 
+bool OpenXRHeadset::update_actions()
+{
+    for (uint32_t index = 0; index < this->controller_button_actions.size(); index++)
+    {
+        
+    }
+
+
+    xrGetActionStateBoolean();
+
+    xrGetActionStateVector2f();
+
+    xrGetActionStatePose();
+
+    return true;
+}
+
+void OpenXRHeadset::update_stage(lava::delta delta_time)
+{
+
+}
+
 bool OpenXRHeadset::begin_frame()
 {
     XrFrameWaitInfo wait_info;
diff --git a/src/headset/openxr_headset.hpp b/src/headset/openxr_headset.hpp
index 659a4cf40c852411cc68871fc6ebbfa2a570736b..19e10eaedfc669c1b833ee207204eceae5231207 100644
--- a/src/headset/openxr_headset.hpp
+++ b/src/headset/openxr_headset.hpp
@@ -50,9 +50,15 @@ private:
     bool create_swapchains();
     void destroy_swapchains();
 
+    bool create_actions();
+    void destroy_actions();
+
     bool poll_events();
     bool process_event(const XrEventDataBuffer& event);
 
+    bool update_actions();
+    void update_stage(lava::delta delta_time);
+
     bool begin_frame();
     bool update_views();
     bool acquire_image(FrameId frame_id);
@@ -77,6 +83,11 @@ private:
     std::vector<XrSwapchain> swapchains;
     std::vector<XrView> views;
 
+    XrActionSet action_set = XR_NULL_HANDLE;
+    std::array<XrAction, 2> controller_transform_actions;
+    std::array<XrAction, 2> controller_thumbsticks_actions;
+    std::array<XrAction, BUTTON_MAX_COUNT> controller_button_actions;
+
     std::array<uint32_t, 2> swapchain_indices;
     std::array<std::vector<VkImage>, 2> swapchain_images;
     std::array<lava::image::ptr, 2> framebuffers;
@@ -92,8 +103,15 @@ private:
     float far_plane = 1000.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;
+
+    std::array<bool, 2> controller_attached;
+    std::array<glm::mat4, 2> controller_transforms;
+    std::array<glm::vec2, 2> controller_thumbsticks;
+    std::array<bool, BUTTON_MAX_COUNT> controller_buttons;
 };
\ No newline at end of file