KRONOS_ENGINE

C++20 · Vulkan · EnTT · Luau

A game engine built to be
read, not guessed at.

Kronos is a Vulkan-renderer engine with a real Forward+ pipeline, a bare EnTT-registry ECS, deterministic Jolt physics, and a sandboxed Luau layer that scripts against the same live state the editor does — no partial Instance-API illusion standing in for either.

kronos_opening.mp4 — engine capture, no post
$ --lang=C++20 --gfx=Vulkan 1.3, dynamic rendering --shadows=4-cascade CSM --ecs=EnTT, sparse-set --physics=Jolt, fixed-tick --script=Luau, sandboxed

Architecture

Four subsystems, one frame graph.

Nothing here is a stub behind a marketing name — each panel below is a real, load-bearing part of the runtime, not an aspirational roadmap item.

Renderer

Forward+, one real frame graph

Opaque pass → cascaded shadow maps → SSAO/DOF/motion-blur → bloom → ACES or AgX tonemap → an optional sampled 3D LUT. Studio's own viewport and an offline capture rig render through the identical path, so what a creator previews is what gets exported.

ECS

A thin wrapper, not a framework

Entities are opaque numbers over a real entt::registry. No inheritance hierarchy, no hidden allocation graph — component storage is exactly what EnTT gives you, queried with view<T...>().

Physics

Jolt, stepped on purpose

A fixed-tick simulation that steps automatically during play, or can be paused and single-stepped by exact, chosen deltas — the same mechanism a tooling script uses to reproduce a physics bug frame by frame.

Scripting

Luau, with a stated boundary

Flat global tables (world, mesh, render, physics) expose numbers, booleans, and strings only. No script ever holds a Vulkan handle, a pipeline, or a raw pointer — that boundary is enforced by what the bindings return, not by a runtime permission check.

Scripting surface

The console runs against the live scene.

Every snippet below is real, callable Luau — the same tables Studio's own Debug Console registers against the running ECS, mesh editor, sequencer, renderer, and physics preview.

1-- entities are plain numbers -- no Instance metatable to guess at
2local id = world.createEntity("Turret")
3world.setPosition(id, 12.5, 0.0, -4.0)
4world.setMaterial(id, "Metal")
5world.applyImpulse(id, 0, 8, 0)
6
7local hit = world.raycast(0, 5, 0,  0, -1, 0,  50)
8if hit then print(hit.entityId, hit.distance) end
1-- real vertex/edge/face topology, live in the viewport
2mesh.beginEditingBox(id, 0.5, 0.5, 0.5)
3mesh.extrudeFace(id, 3, 0.75)
4mesh.insetFace(id, 3, 0.2)
5mesh.setVertexPosition(id, 0, 9, 8, 7)
6-- editVersion bumps -- Modeling Mode re-uploads to the GPU next frame
1-- the same Sequence a creator's own timeline UI edits
2cinematic.addTrack("Camera Rail", "Camera")
3cinematic.addKeyframe("Camera Rail", "railT", 0.0, 0.0, "Bezier")
4cinematic.addKeyframe("Camera Rail", "railT", 4.0, 1.0, "Bezier")
5cinematic.setPlayhead(2.0)
1-- numeric/enum tuning only -- never a raw Vulkan handle
2render.setCinematicMode(true)
3render.setTonemapOperator("agx")
4render.setBloomSettings(1.1, 0.4, 0.7)
5render.loadColorGradingLut("looks/teal_orange.cube")
1-- deterministic, script-triggered steps -- for reproducing a bug exactly
2physics.pause()
3local ok = physics.step(1 / 60)
4assert(ok, "step only runs while playing + paused")
5physics.resume()

Runtime

What the profiler sees.

A representative capture of the runtime's own frame-time graph and scene counters — illustrative, not a live socket into a running instance.

kronos --profile --scene=tntwars_arena simulated
frame time6.42 ms
draw calls318
entities4,096
shadow cascades4
GPU memory612 MB
tonemapAgX

Numbers animate client-side for illustration; they are not streamed from a live Kronos process.