diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 3c3fa16d6..69568f4e9 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -127,7 +127,7 @@ void EmulatedController::LoadDevices() {
     // Initialize TAS devices
     std::transform(tas_button_params.begin(), tas_button_params.end(), tas_button_devices.begin(),
                    Input::CreateDevice<Input::InputDevice>);
-    std::transform(tas_stick_params.begin(), tas_stick_params.begin(), tas_stick_devices.begin(),
+    std::transform(tas_stick_params.begin(), tas_stick_params.end(), tas_stick_devices.begin(),
                    Input::CreateDevice<Input::InputDevice>);
 }
 
@@ -135,7 +135,7 @@ void EmulatedController::LoadTASParams() {
     const auto player_index = NpadIdTypeToIndex(npad_id_type);
     Common::ParamPackage common_params{};
     common_params.Set("engine", "tas");
-    common_params.Set("pad", static_cast<int>(player_index));
+    common_params.Set("port", static_cast<int>(player_index));
     for (auto& param : tas_button_params) {
         param = common_params;
     }
@@ -144,26 +144,26 @@ void EmulatedController::LoadTASParams() {
     }
 
     // TODO(german77): Replace this with an input profile or something better
-    tas_button_params[Settings::NativeButton::A].Set("button", 1 << 0);
-    tas_button_params[Settings::NativeButton::B].Set("button", 1 << 1);
-    tas_button_params[Settings::NativeButton::X].Set("button", 1 << 2);
-    tas_button_params[Settings::NativeButton::Y].Set("button", 1 << 3);
-    tas_button_params[Settings::NativeButton::LStick].Set("button", 1 << 4);
-    tas_button_params[Settings::NativeButton::RStick].Set("button", 1 << 5);
-    tas_button_params[Settings::NativeButton::L].Set("button", 1 << 6);
-    tas_button_params[Settings::NativeButton::R].Set("button", 1 << 7);
-    tas_button_params[Settings::NativeButton::ZL].Set("button", 1 << 8);
-    tas_button_params[Settings::NativeButton::ZR].Set("button", 1 << 9);
-    tas_button_params[Settings::NativeButton::Plus].Set("button", 1 << 10);
-    tas_button_params[Settings::NativeButton::Minus].Set("button", 1 << 11);
-    tas_button_params[Settings::NativeButton::DLeft].Set("button", 1 << 12);
-    tas_button_params[Settings::NativeButton::DUp].Set("button", 1 << 13);
-    tas_button_params[Settings::NativeButton::DRight].Set("button", 1 << 14);
-    tas_button_params[Settings::NativeButton::DDown].Set("button", 1 << 15);
-    tas_button_params[Settings::NativeButton::SL].Set("button", 1 << 16);
-    tas_button_params[Settings::NativeButton::SR].Set("button", 1 << 17);
-    tas_button_params[Settings::NativeButton::Home].Set("button", 1 << 18);
-    tas_button_params[Settings::NativeButton::Screenshot].Set("button", 1 << 19);
+    tas_button_params[Settings::NativeButton::A].Set("button", 0);
+    tas_button_params[Settings::NativeButton::B].Set("button", 1);
+    tas_button_params[Settings::NativeButton::X].Set("button", 2);
+    tas_button_params[Settings::NativeButton::Y].Set("button", 3);
+    tas_button_params[Settings::NativeButton::LStick].Set("button", 4);
+    tas_button_params[Settings::NativeButton::RStick].Set("button", 5);
+    tas_button_params[Settings::NativeButton::L].Set("button", 6);
+    tas_button_params[Settings::NativeButton::R].Set("button", 7);
+    tas_button_params[Settings::NativeButton::ZL].Set("button", 8);
+    tas_button_params[Settings::NativeButton::ZR].Set("button", 9);
+    tas_button_params[Settings::NativeButton::Plus].Set("button", 10);
+    tas_button_params[Settings::NativeButton::Minus].Set("button", 11);
+    tas_button_params[Settings::NativeButton::DLeft].Set("button", 12);
+    tas_button_params[Settings::NativeButton::DUp].Set("button", 13);
+    tas_button_params[Settings::NativeButton::DRight].Set("button", 14);
+    tas_button_params[Settings::NativeButton::DDown].Set("button", 15);
+    tas_button_params[Settings::NativeButton::SL].Set("button", 16);
+    tas_button_params[Settings::NativeButton::SR].Set("button", 17);
+    tas_button_params[Settings::NativeButton::Home].Set("button", 18);
+    tas_button_params[Settings::NativeButton::Screenshot].Set("button", 19);
 
     tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_x", 0);
     tas_stick_params[Settings::NativeAnalog::LStick].Set("axis_y", 1);
diff --git a/src/input_common/drivers/tas_input.cpp b/src/input_common/drivers/tas_input.cpp
index 7e7a1d58f..d2748b240 100644
--- a/src/input_common/drivers/tas_input.cpp
+++ b/src/input_common/drivers/tas_input.cpp
@@ -141,7 +141,7 @@ void Tas::WriteTasFile(std::u8string file_name) {
     }
 }
 
-void Tas::RecordInput(u32 buttons, TasAnalog left_axis, TasAnalog right_axis) {
+void Tas::RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis) {
     last_input = {
         .buttons = buttons,
         .l_axis = FlipAxisY(left_axis),
@@ -195,7 +195,7 @@ void Tas::UpdateThread() {
     }
     if (current_command < script_length) {
         LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length);
-        size_t frame = current_command++;
+        const size_t frame = current_command++;
         for (size_t player_index = 0; player_index < commands.size(); player_index++) {
             TASCommand command{};
             if (frame < commands[player_index].size()) {
@@ -207,8 +207,8 @@ void Tas::UpdateThread() {
                 .port = player_index,
                 .pad = 0,
             };
-            for (std::size_t i = 0; i < sizeof(command.buttons); ++i) {
-                const bool button_status = (command.buttons & (1U << i)) != 0;
+            for (std::size_t i = 0; i < sizeof(command.buttons) * 8; ++i) {
+                const bool button_status = (command.buttons & (1LLU << i)) != 0;
                 const int button = static_cast<int>(i);
                 SetButton(identifier, button, button_status);
             }
@@ -244,14 +244,14 @@ TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
     return {x, y};
 }
 
-u32 Tas::ReadCommandButtons(const std::string& data) const {
+u64 Tas::ReadCommandButtons(const std::string& data) const {
     std::stringstream button_text(data);
     std::string line;
-    u32 buttons = 0;
+    u64 buttons = 0;
     while (std::getline(button_text, line, ';')) {
         for (auto [text, tas_button] : text_to_tas_button) {
             if (text == line) {
-                buttons |= static_cast<u32>(tas_button);
+                buttons |= static_cast<u64>(tas_button);
                 break;
             }
         }
@@ -259,13 +259,14 @@ u32 Tas::ReadCommandButtons(const std::string& data) const {
     return buttons;
 }
 
-std::string Tas::WriteCommandButtons(u32 buttons) const {
+std::string Tas::WriteCommandButtons(u64 buttons) const {
     std::string returns = "";
     for (auto [text_button, tas_button] : text_to_tas_button) {
-        if ((buttons & static_cast<u32>(tas_button)) != 0)
-            returns += fmt::format("{};", text_button.substr(4));
+        if ((buttons & static_cast<u64>(tas_button)) != 0) {
+            returns += fmt::format("{};", text_button);
+        }
     }
-    return returns.empty() ? "NONE" : returns.substr(2);
+    return returns.empty() ? "NONE" : returns;
 }
 
 std::string Tas::WriteCommandAxis(TasAnalog analog) const {
diff --git a/src/input_common/drivers/tas_input.h b/src/input_common/drivers/tas_input.h
index 9fadc118b..5f5c3267c 100644
--- a/src/input_common/drivers/tas_input.h
+++ b/src/input_common/drivers/tas_input.h
@@ -47,7 +47,7 @@ namespace InputCommon::TasInput {
 
 constexpr size_t PLAYER_NUMBER = 10;
 
-enum class TasButton : u32 {
+enum class TasButton : u64 {
     BUTTON_A = 1U << 0,
     BUTTON_B = 1U << 1,
     BUTTON_X = 1U << 2,
@@ -92,7 +92,7 @@ public:
      * @param left_axis: value of the left axis
      * @param right_axis: value of the right axis
      */
-    void RecordInput(u32 buttons, TasAnalog left_axis, TasAnalog right_axis);
+    void RecordInput(u64 buttons, TasAnalog left_axis, TasAnalog right_axis);
 
     // Main loop that records or executes input
     void UpdateThread();
@@ -129,7 +129,7 @@ public:
 
 private:
     struct TASCommand {
-        u32 buttons{};
+        u64 buttons{};
         TasAnalog l_axis{};
         TasAnalog r_axis{};
     };
@@ -164,9 +164,9 @@ private:
      * Parses a string containing the button values. Each button is represented by it's text format
      * specified in text_to_tas_button array
      * @param line: string containing button name with the following format "a;b;c;d..."
-     * @return Returns a u32 with each bit representing the status of a button
+     * @return Returns a u64 with each bit representing the status of a button
      */
-    u32 ReadCommandButtons(const std::string& line) const;
+    u64 ReadCommandButtons(const std::string& line) const;
 
     /**
      * Reset state of all players
@@ -174,11 +174,11 @@ private:
     void ClearInput();
 
     /**
-     * Converts an u32 containing the button status into the text equivalent
+     * Converts an u64 containing the button status into the text equivalent
      * @param buttons: bitfield with the status of the buttons
      * @return Returns a string with the name of the buttons to be written to the file
      */
-    std::string WriteCommandButtons(u32 buttons) const;
+    std::string WriteCommandButtons(u64 buttons) const;
 
     /**
      * Converts an TAS analog object containing the axis status into the text equivalent
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 726f789b7..4b3cd9f3e 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -35,6 +35,7 @@
 #include "core/frontend/framebuffer_layout.h"
 #include "input_common/drivers/keyboard.h"
 #include "input_common/drivers/mouse.h"
+#include "input_common/drivers/tas_input.h"
 #include "input_common/drivers/touch_screen.h"
 #include "input_common/main.h"
 #include "video_core/renderer_base.h"
diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp
index f93a46421..aaca494b8 100644
--- a/src/yuzu/debugger/controller.cpp
+++ b/src/yuzu/debugger/controller.cpp
@@ -7,11 +7,16 @@
 #include <QString>
 #include "common/settings.h"
 #include "core/core.h"
+#include "core/hid/emulated_controller.h"
+#include "input_common/drivers/tas_input.h"
+#include "input_common/main.h"
 #include "yuzu/configuration/configure_input_player_widget.h"
 #include "yuzu/debugger/controller.h"
 
-ControllerDialog::ControllerDialog(Core::System& system, QWidget* parent)
-    : QWidget(parent, Qt::Dialog) {
+ControllerDialog::ControllerDialog(Core::System& system_,
+                                   std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
+                                   QWidget* parent)
+    : QWidget(parent, Qt::Dialog), system{system_}, input_subsystem{input_subsystem_} {
     setObjectName(QStringLiteral("Controller"));
     setWindowTitle(tr("Controller P1"));
     resize(500, 350);
@@ -21,7 +26,7 @@ ControllerDialog::ControllerDialog(Core::System& system, QWidget* parent)
                    Qt::WindowMaximizeButtonHint);
 
     widget = new PlayerControlPreview(this);
-    widget->SetController(system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1));
+    refreshConfiguration();
     QLayout* layout = new QVBoxLayout(this);
     layout->setContentsMargins(0, 0, 0, 0);
     layout->addWidget(widget);
@@ -34,6 +39,22 @@ ControllerDialog::ControllerDialog(Core::System& system, QWidget* parent)
     widget->setFocus();
 }
 
+void ControllerDialog::refreshConfiguration() {
+    UnloadController();
+    auto* player_1 = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Player1);
+    auto* handheld = system.HIDCore().GetEmulatedController(Core::HID::NpadIdType::Handheld);
+    // Display the correct controller
+    controller = handheld->IsConnected() ? handheld : player_1;
+
+    Core::HID::ControllerUpdateCallback engine_callback{
+        .on_change = [this](Core::HID::ControllerTriggerType type) { ControllerUpdate(type); },
+        .is_npad_service = true,
+    };
+    callback_key = controller->SetCallback(engine_callback);
+    widget->SetController(controller);
+    is_controller_set = true;
+}
+
 QAction* ControllerDialog::toggleViewAction() {
     if (toggle_view_action == nullptr) {
         toggle_view_action = new QAction(tr("&Controller P1"), this);
@@ -47,6 +68,10 @@ QAction* ControllerDialog::toggleViewAction() {
 
 void ControllerDialog::UnloadController() {
     widget->UnloadController();
+    if (is_controller_set) {
+        controller->DeleteCallback(callback_key);
+        is_controller_set = false;
+    }
 }
 
 void ControllerDialog::showEvent(QShowEvent* ev) {
@@ -62,3 +87,32 @@ void ControllerDialog::hideEvent(QHideEvent* ev) {
     }
     QWidget::hideEvent(ev);
 }
+
+void ControllerDialog::ControllerUpdate(Core::HID::ControllerTriggerType type) {
+    // TODO(german77): Remove TAS from here
+    switch (type) {
+    case Core::HID::ControllerTriggerType::Button:
+    case Core::HID::ControllerTriggerType::Stick: {
+        const auto buttons_values = controller->GetButtonsValues();
+        const auto stick_values = controller->GetSticksValues();
+        u64 buttons = 0;
+        std::size_t index = 0;
+        for (const auto& button : buttons_values) {
+            buttons |= button.value ? 1LLU << index : 0;
+            index++;
+        }
+        const InputCommon::TasInput::TasAnalog left_axis = {
+            .x = stick_values[Settings::NativeAnalog::LStick].x.value,
+            .y = stick_values[Settings::NativeAnalog::LStick].y.value,
+        };
+        const InputCommon::TasInput::TasAnalog right_axis = {
+            .x = stick_values[Settings::NativeAnalog::RStick].x.value,
+            .y = stick_values[Settings::NativeAnalog::RStick].y.value,
+        };
+        input_subsystem->GetTas()->RecordInput(buttons, left_axis, right_axis);
+        break;
+    }
+    default:
+        break;
+    }
+}
diff --git a/src/yuzu/debugger/controller.h b/src/yuzu/debugger/controller.h
index 33f617b9b..ba4185a4b 100644
--- a/src/yuzu/debugger/controller.h
+++ b/src/yuzu/debugger/controller.h
@@ -11,24 +11,33 @@ class QHideEvent;
 class QShowEvent;
 class PlayerControlPreview;
 
+namespace InputCommon {
+class InputSubsystem;
+}
+
 namespace Core {
 class System;
 }
 
-namespace InputCommon {
-class InputSubsystem;
+namespace Core::HID {
+class EmulatedController;
 }
 
 class ControllerDialog : public QWidget {
     Q_OBJECT
 
 public:
-    explicit ControllerDialog(Core::System& system, QWidget* parent = nullptr);
+    explicit ControllerDialog(Core::System& system_,
+                              std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_,
+                              QWidget* parent = nullptr);
 
     /// Returns a QAction that can be used to toggle visibility of this dialog.
     QAction* toggleViewAction();
 
-    // Disables events from the emulated controller
+    /// Reloads the widget to apply any changes in the configuration
+    void refreshConfiguration();
+
+    /// Disables events from the emulated controller
     void UnloadController();
 
 protected:
@@ -36,6 +45,15 @@ protected:
     void hideEvent(QHideEvent* ev) override;
 
 private:
+    /// Redirects input from the widget to the TAS driver
+    void ControllerUpdate(Core::HID::ControllerTriggerType type);
+
+    int callback_key;
+    bool is_controller_set{};
+    Core::HID::EmulatedController* controller;
+
     QAction* toggle_view_action = nullptr;
     PlayerControlPreview* widget;
+    Core::System& system;
+    std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
 };
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 022d11cc4..56db337a4 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -925,7 +925,7 @@ void GMainWindow::InitializeDebugWidgets() {
     waitTreeWidget->hide();
     debug_menu->addAction(waitTreeWidget->toggleViewAction());
 
-    controller_dialog = new ControllerDialog(*system, this);
+    controller_dialog = new ControllerDialog(*system, input_subsystem, this);
     controller_dialog->hide();
     debug_menu->addAction(controller_dialog->toggleViewAction());