Why we named it Tenon
The name comes from a joint that holds by shape. The engine is a tree of entity nodes, spliced the same way.
A mortise and tenon is one of the oldest joints in timber construction. One member ends in a projecting tongue, the tenon; the other has a matching cavity, the mortise. Fitted together, they hold by shape alone, with no nails and no glue. That is where the name comes from.
A game is a tree
Tenon models a whole game as one tree. The root node is the game, and every part of it is spliced on. The tree is for control. Rendering, physics and audio are backends, and they are not nodes.
The unit is the entity node. Data on a node lives in properties, and a property is a field, not a node of its own. A graft, written in project.tproj, binds one entity as another's subsidiary node. At runtime, attachment instantiates that subsidiary onto a property.
Every entity has one block name, unique in the project. A script addresses it as $BlockName. Compilation resolves that to a NodePath: a fixed index in an AOT build, an inline cache under the VM.
| File | Holds |
|---|---|
.tns | Game logic. No structure. |
project.tproj | The tree, and every graft. |
project.tconf | Configuration. |
.tnm / .tnmc | One level per file, compiled. The filename is the level name. |
All of those files are TML. Release builds compile TenonScript ahead of time through LLVM and link it into the executable. The editor and mods run on the VM, and reload without a rebuild.
What it asks of the machine
Rendering is Vulkan path tracing, with no raster fallback: a physical atmosphere, volumetric clouds, ray-traced shadows, and OIDN denoising. Physics is Jolt, the window and input are SDL3, spatial audio is Steam Audio. All of that ships inside TDeps, alongside TRuntime, TNSCoreRuntime and TNSModRuntime.
The target is AAA. A machine that cannot ray trace is not supported.
A good joint is one where you can't find the nails.