Wraith Engine is a C++/Vulkan game engine runtime paired with a browser-based editor shell. The engine runs headless on a server, streams rendered viewports to browser clients via WebRTC with H.264, and processes editing commands through an authoritative command/event model. One shared runtime supports both local native editing and remotely hosted collaborative sessions.
Collaborative simulation is session-wide: when the simulation host presses Play, Pause, or Stop, every connected collaborator sees the same runtime state. In the current model, the first connected browser collaborator becomes the simulation host for that session, while the headless renderer keeps its reserved local render user.
The browser-facing transport is now encapsulated behind a dedicated WraithNetworking engine module. That module owns HTTP and WebSocket serving through vendored uWebSockets, while the existing WebRTC session path remains intact as the protected media/control layer used for streamed viewports.
The engine's platform boundary now lives under a foundational HAL/ layer. OS, hardware, and compiler-specific functionality routes through AxiomHAL, and higher-level engine modules no longer include platform headers or call platform APIs directly.
Browser (React / Next.js)
├─ Editor UI (outliner, inspector, toolbar, content browser)
└─ WebRTC Viewport Client
↕ commands / H.264 video
AxiomRemoteViewportServer (C++)
└─ EditorSession (authoritative scene state)
├─ Vulkan Renderer (offscreen, per-client)
└─ ScriptHost (Coral .NET 9 / C# scripting)
AxiomCore runtime flow
├─ HAL (`AxiomHAL`: platform, dylib, sockets, file watch, SVG, media backends)
└─ ModuleManager (register / enable / disable / query modules)
├─ Application modules (window polling, layer update, layer render, renderer frame)
├─ Host modules (session transport, script host lifecycle, networking)
├─ Editor feature modules (viewport input, selection, scene render)
├─ Headless overlay module (billboards, colliders, presence, gizmo overlay state)
└─ EditorSession managers (command dispatch, scene-state coordination, physics lifecycle, validation)
Engine
- Vulkan rendering backend with MoltenVK on macOS
- Headless offscreen rendering with H.264 encoding (VideoToolbox on macOS)
- Vulkan backend split into focused subsystems:
VulkanResourceManager,VulkanPipelineLibrary, andVulkanDrawSubmissionSystem, withVulkanRendererBackendacting as the coordinator - Texture uploads now use an async transfer-queue path with semaphore synchronization instead of stalling the main thread with synchronous immediate-submit work
- Hot-path mesh submission path avoids per-frame RTTI and reuses persistent scratch buffers in the Vulkan scene renderer
- Authoritative command/event model for scene mutations
- Foundational engine module/plugin system with lifecycle-managed runtime modules and queryable active state
- Foundational
HAL/platform layer with macOS implementations isolated underHAL/MacOS/ WraithNetworkingmodule for toggleable browser-facing transport, exposing initialization state plus connection metrics for future CVAR/config plumbing- DataModel scene hierarchy — folders, meshes, lights, cameras, actors
- Transform gizmos (translate / scale / rotate) with server-side hit-testing
- Multi-client rendering: each connected user gets their own viewport
- WebRTC streaming to browser
- C# scripting via Coral (.NET 9, hot reload, two trust tiers)
- Scene persistence —
scene.jsonsave/load across restarts - Session-wide Play / Pause / Stop with authoritative edit-snapshot restore
- Runtime-only Jolt physics stepping with pause / resume support
- Physics runtime boot now consumes a lightweight
RuntimeSceneStatesnapshot instead of depending on editor-only scene headers - Default static box collision for imported mesh assets, with load-time migration for older meshes that had no authored physics yet
- Configurable sky background — a two-color vertical gradient (compute-shader blended) or an equirectangular HDR (
.hdr) sampled from a world-space ray; HDR data is preserved end-to-end as float pixels through a v2 cooked.wtexso future image-based lighting can reuse the same asset - Mesh GPU upload now releases CPU-side vertex/index arrays by default; callers can opt back in with
MeshCreateOptions::KeepCpuDatafor the rare CPU-readback/debug workflows that still need them
Browser editor
- Dockable panels: outliner, details/property inspector, content browser, toolbar, Place Actors, script editor, remote viewport
- Window menu to show/hide any panel; panels can be tabbed or floated
- Scene outliner with drag-drop reparenting, inline rename, right-click context menus
- Object lifecycle: create, duplicate, delete
- Per-client gizmo mode (Q / E / R shortcuts)
- Drag meshes into the remote viewport to add them directly
- Light icons render as color-tinted billboards and are selectable from the remote viewport
- User presence and camera visualization
- Script class attachment and hot-reload button
- Inspector-driven physics authoring: body type, collider type, extents/radius, mass, friction, bounce
- Read-only physics visibility for generated mesh children, with inheritance hints pointing back to the authored root mesh object
- World Details panel for editing the skybox: color pickers for the gradient mode, plus an HDR file slot that accepts drag-drop from the content browser, a searchable folder-icon picker listing every
.hdrin the project, or a typed content-relative path - Perspective / Orthographic viewport projection toggle; HDR skybox automatically falls back to gradient in orthographic mode
- Place Actors panel: searchable, category-filtered panel with click-to-place and drag-to-viewport placement; shapes (Cube, Sphere, Cylinder, Cone, Plane) place a Mesh child inside an Actor wrapper; lights, cameras, and generic actors each place their appropriate type
Runtime architecture
IModule/ModuleManagerfoundation for engine-owned feature registration, initialization, update, shutdown, active-state toggling, and future CVAR/config integrationAxiomHALnow acts as the base platform dependency forAxiomCore, which then feeds editor and headless modulesApplicationloop now executes module phases instead of hardcoding subsystem updates- Core app flow, headless host flow, editor viewport flow, and headless overlay rendering are split into focused modules instead of living in monolithic layer/application classes
- Browser-facing networking is now routed through the standalone
WraithNetworkingmodule instead of being bootstrapped inline byAxiomRemoteViewportServer WraithNetworkinguses vendoreduWebSocketsfor HTTP/WebSocket transport while preserving the existing WebRTC communication layer inside the module boundary- First-party OS-specific functionality is now isolated under
HAL/: platform detection, Vulkan loader fallback/dynamic library access, socket helpers, script file watching, SVG rasterization, VideoToolbox H.264, and macOS WebRTC EditorSessionis now a lightweight coordinator that delegates command routing toEditorCommandDispatcher, scene/tree/transform responsibilities toEditorSceneStateManager, physics lifecycle toEditorPhysicsController, and validation toEditorSessionValidationModule- Physics is now isolated behind a runtime-only scene snapshot seam and controller boundary: editor-state extraction stays in the session subsystem, while
PhysicsWorldconsumes only transforms, collider shapes, and material indices - Window minimization and Vulkan surface creation now live behind the
Window/RenderSurfaceabstraction, so the Vulkan backend no longer owns a GLFW window pointer or directly queries GLFW iconification state - Vulkan mesh teardown now routes through a standalone
GPUResourceQueueinstead of reaching into the renderer backend singleton during destruction - The old 1,700+ line Vulkan renderer monolith is gone: resource lifetime, pipeline/layout caching, and draw/submission responsibilities now live in distinct renderer subsystems with narrower ownership boundaries
MeshVertexis now a compact 32-byte layout:vec3 position,vec3 normal,vec2 uv. This trims per-vertex CPU and GPU upload footprint from the previous 40-byte layout while still matching the renderer's Vulkan vertex-input stride.RenderMeshSubmissionno longer stores a per-submissionstd::string Name. Diagnostic names now live in a separate debug-data registry so runtime submissions stay lean.- The Vulkan scene renderer reuses persistent scratch vectors for candidate, opaque, translucent, and compute submission lists.
RenderScenePasses()clears and reuses those buffers each frame instead of allocating fresh vectors in the hot loop. - Mesh type resolution now happens when submissions are built or queued, so the per-frame render loop no longer performs
std::dynamic_pointer_cast<VulkanMesh>for every submitted mesh. VulkanRendererBackendis now GLFW-agnostic for frame begin and presentation setup: minimized-state checks and Vulkan surface creation are forwarded through runtime abstractions instead of hardcoded OS-library calls.VulkanMeshdestruction no longer depends onVulkanRendererBackend::TryGet(). GPU buffer teardown is deferred through a sharedGPUResourceQueue, which keeps resource lifetime independent from the backend singleton.- Renderer responsibilities are now partitioned explicitly:
VulkanResourceManagerowns swapchain/images/buffers/descriptor-backed lifetime,VulkanPipelineLibraryowns raw Vulkan pipeline and layout caching, andVulkanDrawSubmissionSystemowns command recording, queue submission, offscreen capture publication, and async transfer synchronization. - Async texture upload now routes through the transfer queue and a timeline-semaphore wait on graphics submission. This preserves the old rendered output while removing the main-thread stall from material and HDR texture uploads.
- Cooked mesh loading remains backward-compatible with older
.wmeshpayloads, but newly cooked assets use the current packed vertex layout.
VulkanRendererBackend.cppwas reduced from the previous 1,789-line monolith to a thin coordinator layer.- A clean pre-refactor headless baseline and the refactored headless build both rendered the startup scene successfully.
- The first true captured startup-scene frame from baseline and refactor matched byte-for-byte in headless validation, confirming the subsystem split preserved rendered output.
- CMake 3.10+
- Ninja (recommended;
brew install ninja) - C++20 compiler (Clang recommended; Apple Clang 15+ on macOS)
- Vulkan SDK / MoltenVK (macOS:
brew install --cask vulkan-sdk) - Node.js 18+ and pnpm
- macOS: Xcode command-line tools (required for VideoToolbox / WebRTC)
Optional, required only when the corresponding CMake flag is ON:
| Feature | Requirement |
|---|---|
C# Scripting (AXIOM_ENABLE_SCRIPTING) |
.NET 9 SDK (brew install dotnet) |
WebRTC transport (AXIOM_ENABLE_WEBRTC) |
Pre-built WebRTC.framework or libwebrtc for macOS |
By default, single-config CMake generators use Release when no build type is
specified. The checked-in presets still provide explicit debug, release, and
minsizerel configurations, and preset builds run with parallel jobs enabled.
cmake --preset debug
cmake --build build/debugThis is the normal high-performance local command for the browser-facing server
on macOS when using a locally built WebRTC.framework:
cmake --preset release \
-DAXIOM_ENABLE_SCRIPTING=ON \
-DAXIOM_ENABLE_WEBRTC=ON \
-DAXIOM_WEBRTC_FRAMEWORK_PATH=/Users/joshua/webrtc-checkout/src/out/Default/WebRTC.framework
cmake --build build/release --target AxiomRemoteViewportServerThe release preset uses optimized first-party compile defaults, native CPU tuning, IPO/LTO when the toolchain supports it, threaded rendering, the renderer frame task graph, parallel CPU culling, and parallel build jobs.
Physics uses Jolt and is currently enabled by default, but this is the explicit build if you want to guarantee it is on:
cmake --preset debug -DAXIOM_ENABLE_PHYSICS=ON
cmake --build build/debugTo build tests against the physics-enabled runtime:
cmake --preset debug -DBUILD_TESTING=ON -DAXIOM_ENABLE_PHYSICS=ON
cmake --build build/debug
ctest --test-dir build/debugBuild the managed assemblies first, then configure with the scripting flag:
# 1. Build Coral's managed runtime shim
dotnet build ThirdParty/Coral/Coral.Managed/Coral.Managed-Static.csproj -c Debug
# 2. Build the engine API assembly
dotnet build Scripting/WraithEngine.Managed/WraithEngine.Managed.csproj -c Debug
# 3. Configure and build
cmake --preset debug -DAXIOM_ENABLE_SCRIPTING=ON
cmake --build build/debugThe HAL file watcher uses a macOS kqueue backend to detect .dll changes and reload without restarting the server:
cmake --preset debug \
-DAXIOM_ENABLE_SCRIPTING=ON \
-DAXIOM_SCRIPTING_WATCH=ON
cmake --build build/debugSupply paths to a locally built WebRTC library or pre-built framework:
# Using a pre-built WebRTC.framework (macOS)
cmake --preset debug \
-DAXIOM_ENABLE_WEBRTC=ON \
-DAXIOM_WEBRTC_FRAMEWORK_PATH=/path/to/WebRTC.framework
# Using a static libwebrtc binary
cmake --preset debug \
-DAXIOM_ENABLE_WEBRTC=ON \
-DAXIOM_WEBRTC_LIBRARY_PATH=/path/to/libwebrtc.a \
-DAXIOM_WEBRTC_INCLUDE_DIR=/path/to/webrtc/includeNote: the headless browser-facing server now uses vendored uWebSockets for HTTP/WebSocket handling and does not require a separate external HTTP/WebSocket library configuration step.
cmake --preset debug -DBUILD_TESTING=ON
cmake --build build/debug
ctest --test-dir build/debugTo include the scripting tests:
dotnet build ThirdParty/Coral/Coral.Managed/Coral.Managed-Static.csproj -c Debug
dotnet build Scripting/WraithEngine.Managed/WraithEngine.Managed.csproj -c Debug
dotnet build Tests/TestScripts/WraithTestScripts/WraithTestScripts.csproj -c Debug
dotnet build Tests/TestScripts/WraithRestrictedScript/WraithRestrictedScript.csproj -c Debug
cmake --preset debug -DBUILD_TESTING=ON -DAXIOM_ENABLE_SCRIPTING=ON
cmake --build build/debug
ctest --test-dir build/debugAll flags above work with the release preset:
cmake --preset release -DAXIOM_ENABLE_SCRIPTING=ON
cmake --build build/releaseThe release preset is the recommended baseline for runtime performance. Use the debug preset for development diagnostics and tests.
| Option | Type | Default | Description |
|---|---|---|---|
BUILD_TESTING |
BOOL |
OFF |
Build the Google Test suite |
AXIOM_ENABLE_SCRIPTING |
BOOL |
OFF |
Enable the Coral C# scripting host (.NET 9) |
AXIOM_SCRIPTING_WATCH |
BOOL |
OFF |
Auto-reload user scripts on disk change through the HAL file-watcher layer (current macOS backend uses kqueue). Requires AXIOM_ENABLE_SCRIPTING=ON |
AXIOM_SCRIPTING_TRUST_DEFAULT |
STRING |
Restricted |
Default sandbox tier for user scripts. Restricted (hosted — blocks System.Net.*, System.Reflection.Emit, etc.) or Trusted (local dev — full BCL access) |
AXIOM_ENABLE_WEBRTC |
BOOL |
OFF |
Enable the macOS WebRTC transport |
AXIOM_ENABLE_PHYSICS |
BOOL |
ON |
Enable the JoltPhysics runtime simulation seam |
AXIOM_THREADED_RENDER |
BOOL |
ON |
Enable the threaded renderer and worker job system |
AXIOM_PARALLEL_CULL |
BOOL |
ON |
Enable guarded parallel CPU culling in the Vulkan scene renderer |
AXIOM_FRAME_TASK_GRAPH |
BOOL |
ON |
Enable the renderer frame task graph backed by enkiTS jobs |
AXIOM_ENABLE_PERFORMANCE_DEFAULTS |
BOOL |
ON |
Apply optimized compile/link defaults to first-party targets |
AXIOM_OPTIMIZE_FOR_NATIVE_ARCH |
BOOL |
ON |
Tune optimized builds for the host CPU architecture |
AXIOM_ENABLE_IPO |
BOOL |
ON |
Enable interprocedural optimization/LTO for optimized builds when supported |
AXIOM_WEBRTC_FRAMEWORK_PATH |
PATH |
(empty) | Path to a WebRTC.framework bundle (macOS framework variant) |
AXIOM_WEBRTC_LIBRARY_PATH |
FILEPATH |
(empty) | Path to a libwebrtc static/shared binary (non-framework variant) |
AXIOM_WEBRTC_INCLUDE_DIR |
PATH |
(empty) | Include directory for the non-framework libwebrtc variant |
Trust profiles — applies only when AXIOM_ENABLE_SCRIPTING=ON:
| Profile | Intended use | What it blocks |
|---|---|---|
Restricted |
Hosted / cloud sessions | System.Net.*, System.Net.Sockets, System.Reflection.Emit, System.Diagnostics.Process, dynamic assembly loading |
Trusted |
Local dev, native editor | Nothing — full BCL available |
Release build:
./build/release/Headless/AxiomRemoteViewportServer \
--host 127.0.0.1 --port 8080 --width 1280 --height 720Debug build:
./build/debug/Headless/AxiomRemoteViewportServer \
--host 127.0.0.1 --port 8080 --width 1280 --height 720If WebRTC was not enabled at configure time, the server starts but the browser
viewport cannot receive the H.264 WebRTC media path. Reconfigure with
-DAXIOM_ENABLE_WEBRTC=ON and either AXIOM_WEBRTC_FRAMEWORK_PATH or the
AXIOM_WEBRTC_LIBRARY_PATH / AXIOM_WEBRTC_INCLUDE_DIR pair.
At startup, AxiomRemoteViewportServer registers the toggleable WraithNetworking module with ModuleManager. That module owns transport initialization, publishes connection metrics/state snapshots, and keeps the existing WebRTC session logic active behind the same public server API.
cd EditorFrontend
pnpm install
NEXT_PUBLIC_AXIOM_SERVER_ORIGIN=http://127.0.0.1:8080 pnpm devPackage Project now produces a cooked-only package under a project's Package/
directory. The staged layout is:
Package/
AxiomPackagedRuntime
package.wraith.json
Content/
Cooked/
scene.wscene
AssetCookManifest.json
...
Engine/
...
Packages do not ship Content/scene.json or project source assets. The packaged
runtime expects package.wraith.json, Content/Cooked/scene.wscene, the cooked
asset manifest, and all referenced cooked assets to be present.
To run a staged package directly:
./Projects/<project-slug>/Package/AxiomPackagedRuntimeTo test the packaged runtime binary built in build/ against an existing staged
package:
./build/release/Headless/AxiomPackagedRuntime \
--package-root /absolute/path/to/Projects/<project-slug>/PackageOpen http://localhost:3000 in your browser.
./build/release/Editor/AxiomEditor| Path | Contents |
|---|---|
Axiom/ |
Engine library — core, session, renderer, remote transport, scripting host |
HAL/ |
Foundational platform abstraction layer plus concrete macOS implementations |
Editor/ |
Native GLFW + ImGui editor executable |
Headless/ |
Headless runtime and AxiomRemoteViewportServer |
Scripting/WraithEngine.Managed/ |
C# engine API assembly (Script, GameObject, Transform) |
EditorFrontend/ |
React / Next.js browser editor shell |
Content/ |
Shaders, demo assets (.glb), and the persistent scene.json |
Content/Engine/ |
Engine-bundled assets (billboards, primitive shapes); paths prefixed with Engine/ resolve here |
Content/Engine/Primitives/ |
Procedurally generated primitive meshes: Cube, Sphere, Cylinder, Cone, Plane |
Tools/ |
Dev utilities — gen_primitives.py regenerates the primitive GLB assets |
Tests/ |
Google Test suite |
Tests/TestScripts/ |
C# test assemblies for scripting tests |
Docs/ |
Architecture and design documents |
ThirdParty/ |
Vendored dependencies (Coral, spdlog, glfw, fastgltf, glm, …) |
cmake/ |
CMake helper modules |
| Layer | Technology |
|---|---|
| Engine | C++20, CMake, Vulkan |
| Windowing | GLFW, MoltenVK |
| Asset loading | fastgltf (glTF / glb) |
| Streaming | WebRTC, H.264 (VideoToolbox) |
| Scripting | Coral — C++ ↔ .NET 9 host bridge |
| Managed scripting API | C# (.NET 9), WraithEngine.Managed |
| Browser editor | React 19, Next.js, TypeScript |
| Styling | Tailwind CSS, Radix UI |
| Testing | Google Test |
| Logging | spdlog |
MIT — see LICENSE.
