{
"brand": "Ludic",
"tagline": "A compiled language for 2D games, with the entity system in the syntax.",
"repo_url": "https://git.workshopsoft.io/workshopsoft/ludic",
"meta": {
"title": "Ludic — a compiled game language",
"description": "Ludic compiles ahead of time to a native binary. The entity-component system is part of the syntax, the runtime is deterministic fixed-point, and the compiler is written in Ludic — no C is generated, compiled or linked in a build.",
"og_title": "Ludic — a compiled game language",
"og_description": "Ahead-of-time compiled, with an ECS in the syntax and a deterministic fixed-point runtime. The compiler is written in Ludic; no C is generated, compiled or linked in a build."
},
"nav_links": [
{
"label": "Features",
"href": "#features"
},
{
"label": "Examples",
"href": "#showcase"
},
{
"label": "Get started",
"href": "#start"
},
{
"label": "API Reference",
"href": "api.html"
}
],
"ref_nav_links": [
{
"label": "Home",
"href": "index.html"
},
{
"label": "API Reference",
"href": "api.html"
}
],
"hero": {
"title_pre": "A compiled language",
"title_accent": "for 2D games.",
"lead": "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.",
"snippet": "docs/site/snippets/hero.ludic",
"snippet_name": "hello.ludic",
"primary_cta": {
"label": "Get started →",
"href": "#start"
},
"secondary_cta": {
"label": "API Reference",
"href": "api.html"
},
"pipeline": [
".ludic",
"ludicc",
"LLVM IR",
"native binary"
],
"pipeline_note": "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."
},
"features": {
"kicker": "Why Ludic",
"title": "A language shaped around the game.",
"intro": "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.",
"cards": [
{
"title": "ECS in the syntax",
"html": "property, model, and handler are keywords. Query entities with query [A, B, {Tag}] and iterate matches directly — no framework to wire up."
},
{
"title": "Deterministic runtime",
"html": "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."
},
{
"title": "Drawing & named colors",
"html": "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."
},
{
"title": "One self-contained binary",
"html": "A game compiles to a single native executable — no engine to install, no interpreter, no runtime shipped beside it. Build, and run the file."
},
{
"title": "Snapshot save/load",
"html": "save() and load() serialize the entire ECS world — every entity, property and program var — in one call."
},
{
"title": "Scenes & state machines",
"html": "scene/layer/become model mutually-exclusive game states with enter/exit hooks; match/machine/state handle dispatch and per-entity FSMs."
},
{
"title": "Retained UI & assets",
"html": "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."
},
{
"title": "Modules & native calls",
"html": "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."
}
]
},
"showcase": {
"kicker": "Show, don't tell",
"title": "Real programs, one toolchain.",
"intro": "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.",
"samples": [
{
"label": "snake",
"name": "snake.ludic",
"file": "docs/site/snippets/snake.ludic",
"note": "A complete Snake — grid, growth, food, game-over — from primitives. State is named vars, colors are named, and every draw call says what each argument is."
},
{
"label": "scenes",
"name": "scenes.ludic",
"file": "docs/site/snippets/scenes.ludic",
"note": "One active scene at a time. `become` runs the old scene's on-exit and the new one's on-enter; layers draw in declaration order. State is a plain named var."
},
{
"label": "lifecycle",
"name": "toggle.ludic",
"file": "docs/site/snippets/lifecycle.ludic",
"note": "Enable/disable at three scopes — entity, model, handler. Disabling never destroys data: a property's values persist, so a later enable restores them."
},
{
"label": "hello",
"name": "hello.ludic",
"file": "docs/site/snippets/hero.ludic",
"note": "The smallest program that exercises the whole pipeline: properties, a spawn, a queried handler, and a render/quit."
}
]
},
"philosophy": {
"kicker": "The philosophy",
"title": "Batteries in the language, not in a framework.",
"paras": [
"Most game code is plumbing — registering systems, wiring a renderer, threading state through a framework. Ludic makes those decisions once and bakes them into the language, so the code you write is the game's actual logic.",
"That extends to the toolchain. ludicc is written in Ludic and recompiles its own source to a byte-identical binary, rebuilding from a checked-in IR seed with clang and nothing else."
],
"stats": [
{
"big": "0",
"lbl": "lines of C in a build"
},
{
"big": "1",
"lbl": "backend: LLVM IR"
},
{
"big": "=",
"lbl": "byte-exact self-rebuild"
}
]
},
"start": {
"kicker": "Get started",
"title": "One command to install. One to play.",
"intro": "Install the toolchain with a single command — it brings the compiler, the ludic 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.",
"steps": [
{
"title": "Install",
"html": "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."
},
{
"title": "Create a project",
"html": "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."
},
{
"title": "Run it",
"html": "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."
},
{
"title": "Test it, headlessly",
"html": "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."
}
],
"terminal_name": "zsh — ludic",
"terminal": [
{
"comment": "install the toolchain (macOS, Linux)"
},
{
"cmd": "curl -fsSL https://workshopsoft.pages.workshopsoft.io/ludic/install.sh | sh"
},
{
"out": "→ installed ludic 0.4.0 → ~/.ludic"
},
{
"blank": true
},
{
"comment": "a project that builds and plays as it stands"
},
{
"cmd": "ludic new mygame"
},
{
"cmd": "cd mygame"
},
{
"cmd": "ludic run"
},
{
"out": "→ a native window, running your game"
},
{
"blank": true
},
{
"comment": "tests, and a deterministic headless render for CI"
},
{
"cmd": "ludic test"
},
{
"cmd": "ludic build --headless"
},
{
"cmd": "printf 'ddddwww' | ./build/mygame_headless"
},
{
"out": "→ writes build/out.ppm"
}
]
},
"editors": {
"kicker": "Editor experience",
"title": "One language server, every editor.",
"intro": "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.",
"list": [
"VS Code",
"JetBrains IDEs",
"Neovim",
"Helix",
"Emacs",
"Sublime Text",
"Zed"
],
"note": "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."
}
}