A small Vulkan game engine with a separate Electron-based editor.
Showcase video on YouTube, click on the image:
Backend (C++) is a Vulkan PBR forward renderer. Frontend (editor, TypeScript) is an Electron app written in Svelte. These layers communicate between each other using a WebSocket.
Renderer (Vulkan)
.obj, .gltf and other Assimp-supported formatsECS
Lua scripting (sol2)
OnStart(entity) / OnUpdate(entity, dt) lifecycleEntity (get/set position, rotation, scale), Input.isKeyHeld, Time.deltaTimeLuaScriptComponentEditor (Electron + Svelte 5)
Two run modes
ZayaEditor — engine + editor bridge for authoringZayaRuntime — standalone runtime that loads a scene and runs scripts, doesn't load websocket stuffThe engine and editor are separate processes that communicate over a local WebSocket.
The editor sends typed commands (e.g. scriptComponent.scriptPath, input.key, transform edits) as JSON; the engine dispatches them through CommandDispatcher to handlers registered by each system. This split means the editor can be developed, hot-reloaded, and restyled entirely in the JS/Svelte ecosystem without rebuilding C++.
msbuild on PATHFirst-time setup (installs editor dependencies):
pnpm run install:editor
Build the engine and launch the editor:
pnpm run dev:engine
Run only the editor frontend, skipping the engine build (useful for UI work):
pnpm run dev:noengine
Regenerate Visual Studio project files after changing premake5.lua:
pnpm run build:engine
engine/ C++ engine sources and vendored dependencies
src/ First-party engine code
ECS/ Component pools, scene, systems
editor/ WebSocket bridge, EditorLayer, gizmos
runtime/ Standalone runtime entry point
core/ Window, scene manager, config
vendor/ assimp, glfw, imgui, ixwebsocket, lua, sol2, ...
editor/ Electron + Svelte editor
src/main/ Electron main process
src/renderer/ Svelte UI (Hierarchy, Inspector, Asset browser, ...)
assets/ Sample scenes, meshes, shaders (GLSL + SPIR-V), textures, scripts
config/ Engine.ini
-- assets/scripts/example.lua
local speed = 5.0
function OnStart(entity)
print("OnStart: entity " .. tostring(entity.id))
end
function OnUpdate(entity, dt)
local pos = entity:getPosition()
local dx, dz = 0, 0
if Input.isKeyHeld("W") then dz = dz - speed * dt end
if Input.isKeyHeld("S") then dz = dz + speed * dt end
if Input.isKeyHeld("A") then dx = dx - speed * dt end
if Input.isKeyHeld("D") then dx = dx + speed * dt end
if dx ~= 0 or dz ~= 0 then
entity:setPosition(pos.x + dx, pos.y, pos.z + dz)
end
end
Vendored under engine/vendor/: Assimp, GLFW, Dear ImGui + ImGuizmo, IXWebSocket, Lua, sol2, nlohmann/json, simpleini, SPIRV-Reflect, stb. Each retains its original license.
The ECS is based on David Colson's "Making a Simple ECS".
MIT — see LICENSE.