diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 45fde8df25..e742497e12 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -574,6 +574,22 @@ Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMo
     return gyroscope_zero_drift_mode;
 }
 
+void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
+    const auto npad_index_1 = NPadIdToIndex(npad_id_1);
+    const auto npad_index_2 = NPadIdToIndex(npad_id_2);
+
+    // If the controllers at both npad indices form a pair of left and right joycons, merge them.
+    // Otherwise, do nothing.
+    if ((connected_controllers[npad_index_1].type == NPadControllerType::JoyLeft &&
+         connected_controllers[npad_index_2].type == NPadControllerType::JoyRight) ||
+        (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft &&
+         connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) {
+        // Disconnect the joycon at the second id and connect the dual joycon at the first index.
+        DisconnectNPad(npad_id_2);
+        AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1);
+    }
+}
+
 void Controller_NPad::StartLRAssignmentMode() {
     // Nothing internally is used for lr assignment mode. Since we have the ability to set the
     // controller types from boot, it doesn't really matter about showing a selection screen
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 75ce5b7313..ad25c6fbf0 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -134,6 +134,7 @@ public:
     void ConnectAllDisconnectedControllers();
     void ClearAllControllers();
 
+    void MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2);
     void StartLRAssignmentMode();
     void StopLRAssignmentMode();
     bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2);
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 33416b5dd3..bd3c2f26b9 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -671,13 +671,15 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
 
 void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    const auto unknown_1{rp.Pop<u32>()};
-    const auto unknown_2{rp.Pop<u32>()};
+    const auto npad_id_1{rp.Pop<u32>()};
+    const auto npad_id_2{rp.Pop<u32>()};
     const auto applet_resource_user_id{rp.Pop<u64>()};
 
-    LOG_WARNING(Service_HID,
-                "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
-                unknown_1, unknown_2, applet_resource_user_id);
+    LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
+              npad_id_1, npad_id_2, applet_resource_user_id);
+
+    auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
+    controller.MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(RESULT_SUCCESS);