palette.py generates selfhost/backend/stdlib/emit_color.ludic but its template wrote `function color_lookup(name: ptr)`, while the committed, correct source (and the rest of the compiler) uses `pointer` — so running the generator rewrote the file to a drifted version. Emit `pointer`; `python3 tools/docgen/palette.py` now leaves emit_color.ludic byte-identical. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| .claude | ||
| .forgejo | ||
| assets | ||
| changes | ||
| docs | ||
| examples | ||
| runtime | ||
| selfhost | ||
| tools | ||
| .editorconfig | ||
| .gitignore | ||
| CHANGELOG.md | ||
| CODE_OF_CONDUCT.md | ||
| COMPILING.md | ||
| CONTRIBUTING.md | ||
| LANGUAGE.md | ||
| LICENSE | ||
| README.md | ||
| VERSION | ||
Ludic
Ludic is an ahead-of-time compiled language for 2D games with an
entity-component core, a deterministic fixed-point runtime, and its graphics
stack built into the language. ludicc lowers Ludic straight to LLVM IR and
emits a native binary — and ludicc is itself written in Ludic, compiles
its own source to a byte-exact fixpoint, and rebuilds from a checked-in IR seed
with no C compiler in the loop.
.ludic ──► ludicc ──► LLVM IR ──► object ──► native binary
(in Ludic)
No C is generated, compiled or linked in a build. No interpreter, no
transpiler, no C runtime: the framebuffer, sprites, PNG/DEFLATE decoding,
TrueType text, the retained UI, the registers and the RNG are all written in
Ludic (runtime/native/*.ludic); only the window seam — five win_* functions
— is hand-written LLVM IR against the platform ABI (runtime/native/cocoa.ll),
the same floor Rust and Swift stand on.
Backends
| Backend | Status |
|---|---|
| Native 2D (macOS/Cocoa window; headless render for CI) | Shipping — the default bin/x app target. |
| Web / wasm32 | In progress. The browser platform layer is in-tree and documented — runtime/web/ (the <canvas> window platform.js, the libc-free wasm.ll floor) and a Node harness that diffs native vs. wasm frame-for-frame (tools/ludic-web/run.mjs). Emitting wasm was a capability of the retired C compiler and is not yet re-wired on the self-hosted toolchain; see COMPILING.md. |
The same is true of --target cross-compilation and --shared libraries: both
are designed and documented, both lived in the old C compiler, and both are
pending re-implementation on the self-hosted native toolchain.
Quick start
bin/x is the project's task runner — one native binary, written in Ludic and
compiled by Ludic, that replaces every build/test/bootstrap shell script.
Bootstrap it once from a clean checkout (the only step Ludic can't do for
itself, since compiling Ludic needs a compiler) with clang alone:
clang selfhost/ludicc.seed.ll -o bin/ludicc && bin/ludicc tools/x/main.ludic -o bin/x
Then build the whole toolchain and run a game:
bin/x build # -> bin/{ludicc,ludic,x,ludic-fmt,ludic-lsp}
bin/x app examples/games/snake.ludic # compile + open a native window
./build/snake
Render a frame headlessly (what CI checks) — output lands in build/, never the
repo root:
bin/x app examples/games/chronorift.ludic --headless
mkdir -p build && printf 'ddddwww' | ./build/chronorift_headless # writes build/out.ppm
sips -s format png build/out.ppm --out frame.png
Run the suites:
bin/x test # full regression: compiler builds from seed, every example, golden renders
bin/x selfhost-test # correctness + the self-hosting / C-free bootstrap fixpoints
bin/x help # every command
Layout
| Path | What it is |
|---|---|
selfhost/*.ludic |
the compiler, written in Ludic — lexer, parser, and the LLVM-IR backend (ECS storage, queries, spawn, match/machine, UI, scenes, save/load, fixed-point). Built from selfhost/ludicc.seed.ll with clang alone. |
selfhost/golden/renders.sha256 |
text baseline of render-output hashes (replaces binary .ppm fixtures); regenerate with bin/x golden. |
tools/x/*.ludic |
the task runner, written in Ludic — one binary (bin/x) that builds, tests, bootstraps and reseeds the project, replacing every shell script. |
runtime/native/ |
the runtime in Ludic for the native path: core (framebuffer, input, RNG), image/inflate (PNG + DEFLATE, no zlib), truetype (glyph rasterizer), ui (retained widget tree); plus cocoa.ll, the macOS window seam in LLVM IR. |
runtime/web/ |
the browser platform layer: platform.js (the <canvas> window), wasm.ll (the libc-free floor), index.html. |
examples/ |
the example tour, grouped by intent — games/, rendering/, ecs/, events/, networking/, lang/, library/. See examples/README.md. |
tools/ludic-tools/ |
the editor toolchain in Ludic: ludic-fmt (formatter) and ludic-lsp (language server) — one lexer, one vocabulary shared by both. |
tools/editors/ |
plugins for VS Code and JetBrains, plus config for Neovim, Helix, Emacs, Sublime and Zed. |
docs/ |
the per-symbol API reference, regenerated into the docs site. |
COMPILING.md |
the native pipeline: ludicc → LLVM IR → exe, the rt_* runtime protocol, and the (pending) wasm/cross-compile/shared-library paths. |
The design and roadmap material lives on the wiki:
the Events,
Networking,
Scenes,
Lifecycle,
Mobile and
Syntax-redesign
design records, the Bootstrap deep-dive,
and the Luanti roadmap.
The root keeps only this README plus the two user-facing references,
LANGUAGE.md and COMPILING.md.
Language at a glance
program/property(typed fields + defaults) /model(named entity kinds) /system(phase,@annotations,reads/writes).- ECS queries
for (a, b) in query [A, B, {Tag}] where <expr> { … },spawn/despawnwith slot reuse,@-driven lifecycle hooks. - An event bus (
event/emit/@On, cancellable,@Publicpromotion) and networking primitives (@Sync, ownership, RPCs) over a built-in loopback transport — all deterministic, all pure Ludic. scene/layer/become,match/machine+state.- Types
int,fixed(Q16.16),bool,entity,str,byte, typed buffers; a growing namespaced standard library (Math,Vector,Time/Date/Duration/Clock,Random,Hash,Crypto, sorting, …). - Deterministic seeded RNG and
save()/load()snapshot of the whole World. - Built-in 2D: framebuffer primitives, PNG sprites, TrueType text, 9-slice, and
a retained
uiwidget tree declared as data.
See LANGUAGE.md for the full reference, and examples/README.md for runnable demos of each feature.
Editor support
bin/x tools # -> bin/ludic-fmt, bin/ludic-lsp
ludic-lsp speaks LSP 3.17 over stdio, so one binary serves every editor:
context-aware completion, diagnostics from the compiler itself,
go-to-definition and rename across imported files, and comment-preserving
formatting. ludic-fmt is the same formatter as a CLI, for pre-commit hooks and
CI. Both also understand ```ludic fences in Markdown. Plugins and
drop-in config are in tools/editors/.
Chrono Rift — the flagship game
examples/games/chronorift.ludic is a
playable co-op JRPG — overworld, dungeon, random encounters, a turn-based co-op
battle, a boss, an item shop and snapshot save/load — split across modules under
games/chronorift/. Its art is CC0
Kenney sprites, decoded from PNG at runtime by the
Ludic-written PNG/DEFLATE decoder — no zlib, no external dependency.
- Overworld:
WASDmove,Ksave,Lload. - Battle (local co-op): P1/Knight
W/Sselect,Spaceconfirm; P2/MageI/Kselect,Jconfirm.
Status & roadmap
The compiler self-hosts to a byte-exact fixpoint and rebuilds from its IR seed
with no C compiler; the ECS runtime, windowed + headless 2D rendering, the event
bus, the deterministic networking stack, scenes, and save/load are all in place
and covered by bin/x test. CI gates every push and PR on the build, the test
suites, and that C-free fixpoint. The toolchain is versioned with SemVer
(ludicc --version); releases and the CHANGELOG.md are cut from changesets by
x release.
Active work and proposals — the standard library, a fuller type system, rendering/animation/lighting extras, input, filesystem/IO, testing, and re-wiring the web/wasm and cross-compile backends — are tracked as issues, not inlined here:
- Issues & proposals: https://git.workshopsoft.io/workshopsoft/ludic/issues
- Docs site (API reference): https://workshopsoft.pages.workshopsoft.io/ludic/
- Wiki (design & roadmap): https://git.workshopsoft.io/workshopsoft/ludic/wiki
Contributing
See CONTRIBUTING.md for the development loop
(bin/x reseed → bin/x bootstrap-cfree → bin/x test), the code and commit
conventions, and how the bootstrap fixpoint works. Issue and pull-request
templates live under .forgejo/.
License
The Ludic compiler and runtime source are licensed under the
Apache License 2.0 (SPDX-License-Identifier: Apache-2.0) — a
permissive license with an explicit patent grant.
The bundled Kenney art under assets/kenney/ is
third-party and released under CC0 1.0 (public domain); each pack keeps its
own License.txt. Code and assets are licensed separately: Apache-2.0 covers
the source, not the art.