Ludic compiles ahead of time to a native binary — no engine to install, no interpreter, nothing shipped beside the executable. The entity-component system is part of the syntax, the maths is deterministic fixed-point, and ludicc is itself written in Ludic: no C is generated, compiled or linked in a build.
# the smallest program that exercises the whole ECS pipeline
program Hello {
property Position { column: int = 0, row: int = 0 }
property Velocity { delta_x: int = 0, delta_y: int = 0 }
handler SpawnEnemies phase Start {
spawn Enemy { Position { column: 3, row: 4 }, Velocity { delta_x: 1, delta_y: 0 } }
spawn Enemy { Position { column: 10, row: 2 }, Velocity { delta_x: 0, delta_y: 1 } }
}
# a handler declares the entities it touches; the body
# runs once per match, each property bound by name.
@Queries(these: [Position, Velocity])
handler AdvancePositions phase FixedUpdate {
Position.column += Velocity.delta_x
Position.row = Position.row + Velocity.delta_y
}
handler ReportPositions phase Update {
for (moving) in query [Position] {
print(moving.column)
print(moving.row)
}
quit()
}
}
ludicc lowers straight to LLVM IR and links a native binary. There is no C step in between and no runtime to ship alongside — the game is the executable.
The things you normally bolt on — an entity-component system, a renderer, a deterministic clock, save/load — are primitives of the language itself. One way to do each, so there is little to decide and nothing to wire up.
property, model, and handler are keywords. Query entities with query [A, B, {Tag}] and iterate matches directly — no framework to wire up.
Q16.16 fixed-point math and a seeded RNG mean the same inputs produce the same frame — byte-for-byte — every run. Ideal for replays and lockstep netcode.
The Screen API draws rectangles, text and pixels with named arguments; Color.Crimson and 220 more names read like English and cost nothing at runtime.
A game compiles to a single native executable — no engine to install, no interpreter, no runtime shipped beside it. Build, and run the file.
save() and load() serialize the entire ECS world — every entity, property and program var — in one call.
scene/layer/become model mutually-exclusive game states with enter/exit hooks; match/machine/state handle dispatch and per-entity FSMs.
Declare a widget tree as data with ui — panels, labels, buttons, 9-slice skins, keyboard focus. Sprites decode from PNG at runtime; text is real TrueType.
module + @export fn builds a shared library of plain native symbols; extern fn … = "symbol" reaches out to any native library when you need the platform.
The same ludicc that builds a JRPG builds a from-scratch Snake and a scene demo. Nothing is hardcoded to a genre — and every token below links into the reference.
Install the toolchain with a single command — it brings the compiler, the <code>ludic</code> CLI, the engine runtime and the editor tooling, and needs nothing else on your machine but a C toolchain to link with. Then create a project and run it.
The installer downloads a verified toolchain for your platform into ~/.ludic and puts it on your PATH. Nothing else is touched; uninstalling is rm -rf ~/.ludic. On a platform with no prebuilt toolchain it bootstraps from the compiler's own IR seed instead — same result, a few seconds longer.
ludic new mygame writes a manifest, a program that already moves something on screen, a test, and an assets/ directory. There is no scaffolding to choose and no build file to write.
ludic run compiles src/main.ludic to a native binary and launches it. ludic build stops at the binary — one self-contained executable, with nothing to ship beside it.
ludic test compiles and runs every test block in the project. --headless renders frames to a .ppm from piped input, so a game is deterministic enough to diff in CI.
# install the toolchain (macOS, Linux) $ curl -fsSL https://workshopsoft.pages.workshopsoft.io/ludic/install.sh | sh → installed ludic 0.4.0 → ~/.ludic # a project that builds and plays as it stands $ ludic new mygame $ cd mygame $ ludic run → a native window, running your game # tests, and a deterministic headless render for CI $ ludic test $ ludic build --headless $ printf 'ddddwww' | ./build/mygame_headless → writes build/out.ppm
ludic-lsp speaks LSP 3.17 over stdio: context-aware completion, diagnostics from the compiler itself, go-to-definition and rename across imported files, and comment-preserving formatting. It even understands ```ludic fences in Markdown.
Editors spawn ludic lsp — it ships with the toolchain, so there is nothing extra to build or install. The same formatter runs as ludic fmt for pre-commit hooks and CI.