diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index c4944697..ece5b43a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,9 +1,12 @@
 include_directories(".")
 
+set(OLYMPIC_PATH "${CMAKE_CURRENT_SOURCE_DIR}/olympic")
+
 if(TURBINE_BUILD_TYPE STREQUAL "Native")
     add_subdirectory(glutils)
     add_subdirectory(studio)
 endif()
 add_subdirectory(keel)
 add_subdirectory(nostalgia)
+add_subdirectory(olympic)
 add_subdirectory(turbine)
diff --git a/src/nostalgia/modules/CMakeLists.txt b/src/nostalgia/modules/CMakeLists.txt
index 0e7ae642..252bb4a2 100644
--- a/src/nostalgia/modules/CMakeLists.txt
+++ b/src/nostalgia/modules/CMakeLists.txt
@@ -3,7 +3,6 @@
 add_subdirectory(core)
 add_subdirectory(scene)
 
-
 # module libraries
 
 # Keel
@@ -17,6 +16,12 @@ target_link_libraries(
 		NostalgiaCore-Keel
 		NostalgiaScene-Keel
 )
+target_compile_definitions(
+	NostalgiaKeelModules PUBLIC
+		OLYMPIC_PROJECT_NAME="Nostalgia"
+		OLYMPIC_PROJECT_NAMESPACE=nostalgia
+		OLYMPIC_PROJECT_DATADIR=".nostalgia"
+)
 install(
 	FILES
 		keelmodules.hpp
diff --git a/src/nostalgia/studio/CMakeLists.txt b/src/nostalgia/studio/CMakeLists.txt
index 66165ed6..12f46aab 100644
--- a/src/nostalgia/studio/CMakeLists.txt
+++ b/src/nostalgia/studio/CMakeLists.txt
@@ -2,7 +2,7 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
 add_executable(
 	nostalgia-studio WIN32 MACOSX_BUNDLE
-		main.cpp
+		${OLYMPIC_PATH}/applib/applib.cpp
 )
 
 target_link_libraries(
@@ -12,6 +12,14 @@ target_link_libraries(
 		StudioAppLib
 )
 
+install(
+	TARGETS
+		nostalgia-studio
+	RUNTIME DESTINATION
+		${NOSTALGIA_DIST_BIN}
+	BUNDLE DESTINATION .
+)
+
 if(CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT WIN32)
 	# enable LTO
 	set_property(TARGET nostalgia-studio PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
@@ -27,11 +35,3 @@ install(
 	DESTINATION
 		${NOSTALGIA_DIST_RESOURCES}/icons
 )
-
-install(
-	TARGETS
-		nostalgia-studio
-	RUNTIME DESTINATION
-		${NOSTALGIA_DIST_BIN}
-	BUNDLE DESTINATION .
-)
diff --git a/src/nostalgia/studio/main.cpp b/src/nostalgia/studio/main.cpp
deleted file mode 100644
index c371a379..00000000
--- a/src/nostalgia/studio/main.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
- */
-
-#include <nostalgia/modules/keelmodules.hpp>
-#include <nostalgia/modules/studiomodules.hpp>
-
-#include <studioapp/studioapp.hpp>
-
-#ifdef _WIN32
-int WinMain() {
-	auto const argc = __argc;
-	auto const argv = const_cast<const char**>(__argv);
-#else
-int main(int argc, const char **argv) {
-#endif
-	nostalgia::registerKeelModules();
-	nostalgia::registerStudioModules();
-	return studio::main("Nostalgia Studio", ".nostalgia", argc, argv);
-}
diff --git a/src/olympic/CMakeLists.txt b/src/olympic/CMakeLists.txt
new file mode 100644
index 00000000..3f1ac5d2
--- /dev/null
+++ b/src/olympic/CMakeLists.txt
@@ -0,0 +1 @@
+add_subdirectory(applib)
\ No newline at end of file
diff --git a/src/olympic/applib/CMakeLists.txt b/src/olympic/applib/CMakeLists.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/src/olympic/applib/applib.cpp b/src/olympic/applib/applib.cpp
new file mode 100644
index 00000000..977d80a7
--- /dev/null
+++ b/src/olympic/applib/applib.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
+ */
+
+#include <ox/logconn/def.hpp>
+#include <ox/logconn/logconn.hpp>
+
+#ifndef OLYMPIC_PROJECT_NAME
+#define OLYMPIC_PROJECT_NAME "OlympicProject"
+#endif
+
+#ifndef OLYMPIC_PROJECT_NAMESPACE
+#define OLYMPIC_PROJECT_NAMESPACE project
+#endif
+
+#ifndef OLYMPIC_PROJECT_DATADIR
+#define OLYMPIC_PROJECT_DATADIR ".keel"
+#endif
+
+ox::Error run(
+		ox::StringView appName,
+		ox::StringView projectDataDir,
+		int argc,
+		const char **argv) noexcept;
+
+namespace olympic {
+[[nodiscard]]
+ox::StringView appName() noexcept;
+}
+
+namespace OLYMPIC_PROJECT_NAMESPACE {
+void registerKeelModules() noexcept;
+void registerStudioModules() noexcept;
+}
+
+#ifdef _WIN32
+int WinMain() {
+	auto const argc = __argc;
+	auto const argv = const_cast<const char**>(__argv);
+#else
+int main(int argc, const char **argv) {
+#endif
+	OX_INIT_DEBUG_LOGGER(loggerConn, olympic::appName())
+	OLYMPIC_PROJECT_NAMESPACE::registerKeelModules();
+	OLYMPIC_PROJECT_NAMESPACE::registerStudioModules();
+	auto const err = run(olympic::appName(), OLYMPIC_PROJECT_DATADIR, argc, argv);
+	oxAssert(err, "Something went wrong...");
+	if (err) {
+		oxErrf("Failure: {}\n", toStr(err));
+	}
+	return static_cast<int>(err);
+}
diff --git a/src/olympic/applib/applib.hpp b/src/olympic/applib/applib.hpp
new file mode 100644
index 00000000..c5bccfff
--- /dev/null
+++ b/src/olympic/applib/applib.hpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved.
+ */
+
+#pragma once
+
+#include <ox/std/stringview.hpp>
+
+#define OLYMPIC_APP_PROVIDERS(projectName, projectNamespace) \
+namespace projectNamespace { \
+void registerKeelModules() noexcept; \
+void registerStudioModules() noexcept; \
+} \
+namespace olympic { \
+ox::StringView appName() noexcept {return projectName OLYMPIC_APP;} \
+void loadKeelModules() noexcept {projectNamespace::registerKeelModules();} \
+void loadStudioModules() noexcept {projectNamespace::registerStudioModules();} \
+}
diff --git a/src/studio/applib/include/studioapp/studioapp.hpp b/src/studio/applib/include/studioapp/studioapp.hpp
index 3d74e02e..b514e77a 100644
--- a/src/studio/applib/include/studioapp/studioapp.hpp
+++ b/src/studio/applib/include/studioapp/studioapp.hpp
@@ -17,9 +17,9 @@ struct StudioOptions {
 	ox::String projectDataDir;
 };
 
-int main(StudioOptions&&);
+ox::Error run(StudioOptions&&);
 
-int main(
+ox::Error run(
 		ox::CRStringView appName,
 		ox::CRStringView projectDataDir,
 		int argc,
diff --git a/src/studio/applib/src/main.cpp b/src/studio/applib/src/main.cpp
index 5709e56a..e04bf601 100644
--- a/src/studio/applib/src/main.cpp
+++ b/src/studio/applib/src/main.cpp
@@ -62,7 +62,7 @@ static ox::Error runApp(
 	return err;
 }
 
-int main(
+ox::Error run(
 		ox::CRStringView appName,
 		ox::CRStringView projectDataDir,
 		int,
@@ -77,12 +77,27 @@ int main(
 	// run app
 	const auto err = runApp(appName, projectDataDir, ox::UniquePtr<ox::FileSystem>(nullptr));
 	oxAssert(err, "Something went wrong...");
-	return static_cast<int>(err);
+	return err;
 }
 
-int main(StudioOptions &&opts, int argc = 0, const char **argv = nullptr) {
-	return main(opts.appName, opts.projectDataDir, argc, argv);
+ox::Error run(StudioOptions &&opts, int argc = 0, const char **argv = nullptr) {
+	return run(opts.appName, opts.projectDataDir, argc, argv);
 }
 
 }
 
+namespace olympic {
+[[nodiscard]]
+ox::StringView appName() noexcept {
+	return "Nostalgia Studio";
+}
+}
+
+ox::Error run(
+		ox::StringView appName,
+		ox::StringView projectDataDir,
+		int argc,
+		const char **argv) noexcept {
+	return studio::run(appName, projectDataDir, argc, argv);
+}
+