diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index 4b83eeb28..0bc611649 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -47,6 +47,7 @@ namespace Log {
     SUB(Service, NDM)                                                                              \
     SUB(Service, NFC)                                                                              \
     SUB(Service, NIM)                                                                              \
+    SUB(Service, NS)                                                                               \
     SUB(Service, NWM)                                                                              \
     SUB(Service, CAM)                                                                              \
     SUB(Service, CECD)                                                                             \
diff --git a/src/common/logging/log.h b/src/common/logging/log.h
index fe4dfed69..f36642c38 100644
--- a/src/common/logging/log.h
+++ b/src/common/logging/log.h
@@ -64,6 +64,7 @@ enum class Class : ClassType {
     Service_NDM,       ///< The NDM (Network daemon manager) service
     Service_NFC,       ///< The NFC service
     Service_NIM,       ///< The NIM (Network interface manager) service
+    Service_NS,        ///< The NS (Nintendo User Interface Shell) service
     Service_NWM,       ///< The NWM (Network wlan manager) service
     Service_CAM,       ///< The CAM (Camera) service
     Service_CECD,      ///< The CECD (StreetPass) service
diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp
index cbebc6c9d..fbc080d76 100644
--- a/src/core/hle/service/apt/apt.cpp
+++ b/src/core/hle/service/apt/apt.cpp
@@ -21,6 +21,7 @@
 #include "core/hle/service/apt/bcfnt/bcfnt.h"
 #include "core/hle/service/cfg/cfg.h"
 #include "core/hle/service/fs/archive.h"
+#include "core/hle/service/ns/ns.h"
 #include "core/hle/service/ptm/ptm.h"
 #include "core/hle/service/service.h"
 #include "core/hw/aes/ccm.h"
@@ -83,6 +84,41 @@ struct AppletSlotData {
 // Holds data about the concurrently running applets in the system.
 static std::array<AppletSlotData, NumAppletSlot> applet_slots = {};
 
+struct AppletTitleData {
+    // There are two possible applet ids for each applet.
+    std::array<AppletId, 2> applet_ids;
+
+    // There's a specific TitleId per region for each applet.
+    static constexpr size_t NumRegions = 7;
+    std::array<u64, NumRegions> title_ids;
+};
+
+static constexpr size_t NumApplets = 29;
+static constexpr std::array<AppletTitleData, NumApplets> applet_titleids = {{
+    {AppletId::HomeMenu, AppletId::None, 0x4003000008202, 0x4003000008F02, 0x4003000009802,
+     0x4003000008202, 0x400300000A102, 0x400300000A902, 0x400300000B102},
+    {AppletId::SoftwareKeyboard1, AppletId::SoftwareKeyboard2, 0x400300000C002, 0x400300000C802,
+     0x400300000D002, 0x400300000C002, 0x400300000D802, 0x400300000DE02, 0x400300000E402},
+    {AppletId::Error, AppletId::Error2, 0x400300000C502, 0x400300000C502, 0x400300000C502,
+     0x400300000C502, 0x400300000CF02, 0x400300000CF02, 0x400300000CF02},
+    {AppletId::Ed1, AppletId::Ed2, 0x400300000C102, 0x400300000C902, 0x400300000D102,
+     0x400300000C102, 0x400300000D902, 0x400300000DF02, 0x400300000E502},
+    // TODO(Subv): Fill in the rest of the titleids
+}};
+
+static u64 GetTitleIdForApplet(AppletId id) {
+    ASSERT_MSG(id != AppletId::None, "Invalid applet id");
+
+    auto itr = std::find_if(applet_titleids.begin(), applet_titleids.end(),
+                            [id](const AppletTitleData& data) {
+                                return data.applet_ids[0] == id || data.applet_ids[1] == id;
+                            });
+
+    ASSERT_MSG(itr != applet_titleids.end(), "Unknown applet id");
+
+    return itr->title_ids[CFG::GetRegionValue()];
+}
+
 // This overload returns nullptr if no applet with the specified id has been started.
 static AppletSlotData* GetAppletSlotData(AppletId id) {
     auto GetSlot = [](AppletSlot slot) -> AppletSlotData* {
@@ -771,8 +807,29 @@ void PrepareToStartLibraryApplet(Service::Interface* self) {
 
     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 
-    // TODO(Subv): Launch the requested applet application.
+    // The real APT service returns an error if there's a pending APT parameter when this function
+    // is called.
+    if (next_parameter) {
+        rb.Push(ResultCode(ErrCodes::ParameterPresent, ErrorModule::Applet,
+                           ErrorSummary::InvalidState, ErrorLevel::Status));
+        return;
+    }
 
+    const auto& slot = applet_slots[static_cast<size_t>(AppletSlot::LibraryApplet)];
+
+    if (slot.registered) {
+        rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
+                           ErrorSummary::InvalidState, ErrorLevel::Status));
+        return;
+    }
+
+    auto process = NS::LaunchTitle(FS::MediaType::NAND, GetTitleIdForApplet(applet_id));
+    if (process) {
+        rb.Push(RESULT_SUCCESS);
+        return;
+    }
+
+    // If we weren't able to load the native applet title, try to fallback to an HLE implementation.
     auto applet = HLE::Applets::Applet::Get(applet_id);
     if (applet) {
         LOG_WARNING(Service_APT, "applet has already been started id=%08X",
@@ -805,8 +862,21 @@ void PreloadLibraryApplet(Service::Interface* self) {
 
     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 
-    // TODO(Subv): Launch the requested applet application.
+    const auto& slot = applet_slots[static_cast<size_t>(AppletSlot::LibraryApplet)];
 
+    if (slot.registered) {
+        rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet,
+                           ErrorSummary::InvalidState, ErrorLevel::Status));
+        return;
+    }
+
+    auto process = NS::LaunchTitle(FS::MediaType::NAND, GetTitleIdForApplet(applet_id));
+    if (process) {
+        rb.Push(RESULT_SUCCESS);
+        return;
+    }
+
+    // If we weren't able to load the native applet title, try to fallback to an HLE implementation.
     auto applet = HLE::Applets::Applet::Get(applet_id);
     if (applet) {
         LOG_WARNING(Service_APT, "applet has already been started id=%08X",
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 9e19c38bf..9616cc8f6 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -2,12 +2,35 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include <cinttypes>
+#include "core/hle/service/am/am.h"
 #include "core/hle/service/ns/ns.h"
 #include "core/hle/service/ns/ns_s.h"
+#include "core/loader/loader.h"
 
 namespace Service {
 namespace NS {
 
+Kernel::SharedPtr<Kernel::Process> LaunchTitle(FS::MediaType media_type, u64 title_id) {
+    std::string path = AM::GetTitleContentPath(media_type, title_id);
+    auto loader = Loader::GetLoader(path);
+
+    if (!loader) {
+        LOG_WARNING(Service_NS, "Could not find .app for title 0x%016" PRIx64, title_id);
+        return nullptr;
+    }
+
+    Kernel::SharedPtr<Kernel::Process> process;
+    Loader::ResultStatus result = loader->Load(process);
+
+    if (result != Loader::ResultStatus::Success) {
+        LOG_WARNING(Service_NS, "Error loading .app for title 0x%016" PRIx64, title_id);
+        return nullptr;
+    }
+
+    return process;
+}
+
 void InstallInterfaces(SM::ServiceManager& service_manager) {
     std::make_shared<NS_S>()->InstallAsService(service_manager);
 }
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index c3d67d98c..6fb171c1a 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -4,11 +4,15 @@
 
 #pragma once
 
+#include "core/hle/kernel/process.h"
 #include "core/hle/service/service.h"
 
 namespace Service {
 namespace NS {
 
+/// Loads and launches the title identified by title_id in the specified media type.
+Kernel::SharedPtr<Kernel::Process> LaunchTitle(FS::MediaType media_type, u64 title_id);
+
 /// Registers all NS services with the specified service manager.
 void InstallInterfaces(SM::ServiceManager& service_manager);