diff --git a/src/core/hid/emulated_console.cpp b/src/core/hid/emulated_console.cpp
index aac45907d..fb7e5802a 100644
--- a/src/core/hid/emulated_console.cpp
+++ b/src/core/hid/emulated_console.cpp
@@ -19,27 +19,26 @@ void EmulatedConsole::ReloadFromSettings() {
 }
 
 void EmulatedConsole::SetTouchParams() {
-    // TODO(german77): Support any number of fingers
     std::size_t index = 0;
 
-    // Hardcode mouse, touchscreen and cemuhook parameters
+    // We can't use mouse as touch if native mouse is enabled
     if (!Settings::values.mouse_enabled) {
-        // We can't use mouse as touch if native mouse is enabled
         touch_params[index++] = Common::ParamPackage{"engine:mouse,axis_x:10,axis_y:11,button:0"};
     }
 
     touch_params[index++] =
-        Common::ParamPackage{"engine:touch,axis_x:0,axis_y:1,button:0,touch_id:0"};
+        Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536"};
     touch_params[index++] =
-        Common::ParamPackage{"engine:touch,axis_x:2,axis_y:3,button:1,touch_id:1"};
-    touch_params[index++] =
-        Common::ParamPackage{"engine:touch,axis_x:4,axis_y:5,button:2,touch_id:2"};
-    touch_params[index++] =
-        Common::ParamPackage{"engine:touch,axis_x:6,axis_y:7,button:3,touch_id:3"};
-    touch_params[index++] =
-        Common::ParamPackage{"engine:cemuhookudp,axis_x:17,axis_y:18,button:65536,touch_id:0"};
-    touch_params[index++] =
-        Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072,touch_id:1"};
+        Common::ParamPackage{"engine:cemuhookudp,axis_x:19,axis_y:20,button:131072"};
+
+    for (int i = 0; i < static_cast<int>(MaxActiveTouchInputs); i++) {
+        Common::ParamPackage touchscreen_param{};
+        touchscreen_param.Set("engine", "touch");
+        touchscreen_param.Set("axis_x", i * 2);
+        touchscreen_param.Set("axis_y", (i * 2) + 1);
+        touchscreen_param.Set("button", i);
+        touch_params[index++] = touchscreen_param;
+    }
 
     const auto button_index =
         static_cast<u64>(Settings::values.touch_from_button_map_index.GetValue());
@@ -47,7 +46,7 @@ void EmulatedConsole::SetTouchParams() {
 
     // Map the rest of the fingers from touch from button configuration
     for (const auto& config_entry : touch_buttons) {
-        if (index >= touch_params.size()) {
+        if (index >= MaxTouchDevices) {
             continue;
         }
         Common::ParamPackage params{config_entry};
@@ -60,7 +59,6 @@ void EmulatedConsole::SetTouchParams() {
         touch_button_params.Set("button", params.Serialize());
         touch_button_params.Set("x", x);
         touch_button_params.Set("y", y);
-        touch_button_params.Set("touch_id", static_cast<int>(index));
         touch_params[index] = touch_button_params;
         index++;
     }
@@ -178,12 +176,38 @@ void EmulatedConsole::SetMotion(const Common::Input::CallbackStatus& callback) {
 }
 
 void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index) {
-    if (index >= console.touch_values.size()) {
+    if (index >= MaxTouchDevices) {
         return;
     }
     std::unique_lock lock{mutex};
 
-    console.touch_values[index] = TransformToTouch(callback);
+    const auto touch_input = TransformToTouch(callback);
+    auto touch_index = GetIndexFromFingerId(index);
+    bool is_new_input = false;
+
+    if (!touch_index.has_value() && touch_input.pressed.value) {
+        touch_index = GetNextFreeIndex();
+        is_new_input = true;
+    }
+
+    // No free entries or invalid state. Ignore input
+    if (!touch_index.has_value()) {
+        return;
+    }
+
+    auto& touch_value = console.touch_values[touch_index.value()];
+
+    if (is_new_input) {
+        touch_value.pressed.value = true;
+        touch_value.id = static_cast<u32>(index);
+    }
+
+    touch_value.x = touch_input.x;
+    touch_value.y = touch_input.y;
+
+    if (!touch_input.pressed.value) {
+        touch_value.pressed.value = false;
+    }
 
     if (is_configuring) {
         lock.unlock();
@@ -191,11 +215,15 @@ void EmulatedConsole::SetTouch(const Common::Input::CallbackStatus& callback, st
         return;
     }
 
-    // TODO(german77): Remap touch id in sequential order
-    console.touch_state[index] = {
-        .position = {console.touch_values[index].x.value, console.touch_values[index].y.value},
-        .id = static_cast<u32>(console.touch_values[index].id),
-        .pressed = console.touch_values[index].pressed.value,
+    // Touch outside allowed range. Ignore input
+    if (touch_index.value() >= MaxActiveTouchInputs) {
+        return;
+    }
+
+    console.touch_state[touch_index.value()] = {
+        .position = {touch_value.x.value, touch_value.y.value},
+        .id = static_cast<u32>(touch_index.value()),
+        .pressed = touch_input.pressed.value,
     };
 
     lock.unlock();
@@ -222,6 +250,28 @@ TouchFingerState EmulatedConsole::GetTouch() const {
     return console.touch_state;
 }
 
+std::optional<std::size_t> EmulatedConsole::GetIndexFromFingerId(std::size_t finger_id) const {
+    for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
+        const auto& finger = console.touch_values[index];
+        if (!finger.pressed.value) {
+            continue;
+        }
+        if (finger.id == static_cast<int>(finger_id)) {
+            return index;
+        }
+    }
+    return std::nullopt;
+}
+
+std::optional<std::size_t> EmulatedConsole::GetNextFreeIndex() const {
+    for (std::size_t index = 0; index < MaxTouchDevices; ++index) {
+        if (!console.touch_values[index].pressed.value) {
+            return index;
+        }
+    }
+    return std::nullopt;
+}
+
 void EmulatedConsole::TriggerOnChange(ConsoleTriggerType type) {
     std::scoped_lock lock{callback_mutex};
     for (const auto& poller_pair : callback_list) {
diff --git a/src/core/hid/emulated_console.h b/src/core/hid/emulated_console.h
index 1c510cd19..697ecd2d6 100644
--- a/src/core/hid/emulated_console.h
+++ b/src/core/hid/emulated_console.h
@@ -7,6 +7,7 @@
 #include <functional>
 #include <memory>
 #include <mutex>
+#include <optional>
 #include <unordered_map>
 
 #include "common/common_funcs.h"
@@ -20,6 +21,8 @@
 #include "core/hid/motion_input.h"
 
 namespace Core::HID {
+static constexpr std::size_t MaxTouchDevices = 32;
+static constexpr std::size_t MaxActiveTouchInputs = 16;
 
 struct ConsoleMotionInfo {
     Common::Input::MotionStatus raw_status{};
@@ -27,13 +30,13 @@ struct ConsoleMotionInfo {
 };
 
 using ConsoleMotionDevices = std::unique_ptr<Common::Input::InputDevice>;
-using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, 16>;
+using TouchDevices = std::array<std::unique_ptr<Common::Input::InputDevice>, MaxTouchDevices>;
 
 using ConsoleMotionParams = Common::ParamPackage;
-using TouchParams = std::array<Common::ParamPackage, 16>;
+using TouchParams = std::array<Common::ParamPackage, MaxTouchDevices>;
 
 using ConsoleMotionValues = ConsoleMotionInfo;
-using TouchValues = std::array<Common::Input::TouchStatus, 16>;
+using TouchValues = std::array<Common::Input::TouchStatus, MaxTouchDevices>;
 
 struct TouchFinger {
     u64 last_touch{};
@@ -55,7 +58,7 @@ struct ConsoleMotion {
     bool is_at_rest{};
 };
 
-using TouchFingerState = std::array<TouchFinger, 16>;
+using TouchFingerState = std::array<TouchFinger, MaxActiveTouchInputs>;
 
 struct ConsoleStatus {
     // Data from input_common
@@ -166,6 +169,10 @@ private:
      */
     void SetTouch(const Common::Input::CallbackStatus& callback, std::size_t index);
 
+    std::optional<std::size_t> GetIndexFromFingerId(std::size_t finger_id) const;
+
+    std::optional<std::size_t> GetNextFreeIndex() const;
+
     /**
      * Triggers a callback that something has changed on the console status
      * @param type Input type of the event to trigger
diff --git a/src/core/hid/input_converter.cpp b/src/core/hid/input_converter.cpp
index 5d8b75b50..502692875 100644
--- a/src/core/hid/input_converter.cpp
+++ b/src/core/hid/input_converter.cpp
@@ -200,9 +200,6 @@ Common::Input::TouchStatus TransformToTouch(const Common::Input::CallbackStatus&
     x = std::clamp(x, 0.0f, 1.0f);
     y = std::clamp(y, 0.0f, 1.0f);
 
-    // Limit id to maximum number of fingers
-    status.id = std::clamp(status.id, 0, 16);
-
     if (status.pressed.inverted) {
         status.pressed.value = !status.pressed.value;
     }
diff --git a/src/input_common/helpers/touch_from_buttons.cpp b/src/input_common/helpers/touch_from_buttons.cpp
index da4a3dca5..003a38da5 100644
--- a/src/input_common/helpers/touch_from_buttons.cpp
+++ b/src/input_common/helpers/touch_from_buttons.cpp
@@ -10,8 +10,8 @@ namespace InputCommon {
 class TouchFromButtonDevice final : public Common::Input::InputDevice {
 public:
     using Button = std::unique_ptr<Common::Input::InputDevice>;
-    TouchFromButtonDevice(Button button_, int touch_id_, float x_, float y_)
-        : button(std::move(button_)), touch_id(touch_id_), x(x_), y(y_) {
+    TouchFromButtonDevice(Button button_, float x_, float y_)
+        : button(std::move(button_)), x(x_), y(y_) {
         last_button_value = false;
         button->SetCallback({
             .on_change =
@@ -34,7 +34,6 @@ public:
             .pressed = button_status,
             .x = {},
             .y = {},
-            .id = touch_id,
         };
         status.x.properties = properties;
         status.y.properties = properties;
@@ -62,7 +61,6 @@ public:
 private:
     Button button;
     bool last_button_value;
-    const int touch_id;
     const float x;
     const float y;
     const Common::Input::AnalogProperties properties{0.0f, 1.0f, 0.5f, 0.0f, false};
@@ -73,10 +71,9 @@ std::unique_ptr<Common::Input::InputDevice> TouchFromButton::Create(
     const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
     auto button = Common::Input::CreateDeviceFromString<Common::Input::InputDevice>(
         params.Get("button", null_engine));
-    const auto touch_id = params.Get("touch_id", 0);
     const float x = params.Get("x", 0.0f) / 1280.0f;
     const float y = params.Get("y", 0.0f) / 720.0f;
-    return std::make_unique<TouchFromButtonDevice>(std::move(button), touch_id, x, y);
+    return std::make_unique<TouchFromButtonDevice>(std::move(button), x, y);
 }
 
 } // namespace InputCommon
diff --git a/src/input_common/input_poller.cpp b/src/input_common/input_poller.cpp
index 4ac182147..fb8be42e2 100644
--- a/src/input_common/input_poller.cpp
+++ b/src/input_common/input_poller.cpp
@@ -229,13 +229,12 @@ private:
 
 class InputFromTouch final : public Common::Input::InputDevice {
 public:
-    explicit InputFromTouch(PadIdentifier identifier_, int touch_id_, int button_, bool toggle_,
-                            bool inverted_, int axis_x_, int axis_y_,
-                            Common::Input::AnalogProperties properties_x_,
+    explicit InputFromTouch(PadIdentifier identifier_, int button_, bool toggle_, bool inverted_,
+                            int axis_x_, int axis_y_, Common::Input::AnalogProperties properties_x_,
                             Common::Input::AnalogProperties properties_y_,
                             InputEngine* input_engine_)
-        : identifier(identifier_), touch_id(touch_id_), button(button_), toggle(toggle_),
-          inverted(inverted_), axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_),
+        : identifier(identifier_), button(button_), toggle(toggle_), inverted(inverted_),
+          axis_x(axis_x_), axis_y(axis_y_), properties_x(properties_x_),
           properties_y(properties_y_), input_engine(input_engine_) {
         UpdateCallback engine_callback{[this]() { OnChange(); }};
         const InputIdentifier button_input_identifier{
@@ -271,8 +270,7 @@ public:
     }
 
     Common::Input::TouchStatus GetStatus() const {
-        Common::Input::TouchStatus status;
-        status.id = touch_id;
+        Common::Input::TouchStatus status{};
         status.pressed = {
             .value = input_engine->GetButton(identifier, button),
             .inverted = inverted,
@@ -307,7 +305,6 @@ public:
 
 private:
     const PadIdentifier identifier;
-    const int touch_id;
     const int button;
     const bool toggle;
     const bool inverted;
@@ -919,7 +916,6 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTriggerDevice(
 
 std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice(
     const Common::ParamPackage& params) {
-    const auto touch_id = params.Get("touch_id", 0);
     const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
     const auto range = std::clamp(params.Get("range", 1.0f), 0.25f, 1.50f);
     const auto threshold = std::clamp(params.Get("threshold", 0.5f), 0.0f, 1.0f);
@@ -954,8 +950,8 @@ std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateTouchDevice(
     input_engine->PreSetAxis(identifier, axis_x);
     input_engine->PreSetAxis(identifier, axis_y);
     input_engine->PreSetButton(identifier, button);
-    return std::make_unique<InputFromTouch>(identifier, touch_id, button, toggle, inverted, axis_x,
-                                            axis_y, properties_x, properties_y, input_engine.get());
+    return std::make_unique<InputFromTouch>(identifier, button, toggle, inverted, axis_x, axis_y,
+                                            properties_x, properties_y, input_engine.get());
 }
 
 std::unique_ptr<Common::Input::InputDevice> InputFactory::CreateBatteryDevice(