{ "brand": "Ludic", "tagline": "The opinionated, compiled language for 2D games.", "repo_url": "https://git.workshopsoft.io/workshopsoft/ludic", "meta": { "title": "Ludic — the opinionated game language", "description": "Ludic is an opinionated, compiled language for 2D games. The entity system, rendering, named colors, input, deterministic math and save/load are part of the language — so you build a game, not a framework.", "og_title": "Ludic — the opinionated game language", "og_description": "An opinionated, compiled language for 2D games. The ECS, rendering, colors, input and save/load are built in — build a game, not a framework." }, "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": { "pill": "Opinionated · Batteries-included · No framework", "title_pre": "Build a game,", "title_accent": "not a framework.", "lead": "Ludic is an opinionated, compiled language for 2D games. The entity system, drawing, named colors, input, deterministic math and save/load aren't libraries you wire up — they're part of the language. You write the game; there's nothing to assemble first.", "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", "optimized IR", "native binary"], "pipeline_note": "One step from your code to a native binary. No engine to install, no interpreter, no runtime to ship alongside it — 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 opinionated way to do each, so there's little to decide and nothing to wire up.", "cards": [ { "icon": "🧩", "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." }, { "icon": "⏱️", "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." }, { "icon": "🎨", "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." }, { "icon": "📦", "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." }, { "icon": "💾", "title": "Snapshot save/load", "html": "save() and load() serialize the entire ECS world — every entity, property and program var — in one call." }, { "icon": "🎬", "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." }, { "icon": "🖼️", "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." }, { "icon": "🔌", "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": "No framework. No glue. Just the game.", "paras": [ "Most game code is plumbing — registering systems, wiring a renderer, threading state through a framework. Ludic makes those decisions for you and bakes them into the language, so the code you write is the game's actual logic.", "Opinionated on purpose: one clear way to spawn an entity, draw a frame, name a color, run a scene. Less to choose, less to learn, less to maintain." ], "stats": [ { "big": "0", "lbl": "engines to install" }, { "big": "1", "lbl": "clear way to do each thing" }, { "big": "=", "lbl": "deterministic frames" } ] }, "start": { "kicker": "Get started", "title": "From clone to a native window.", "intro": "Build the toolchain once, then your game. Everything runs from the repo root.", "steps": [ { "title": "Build the toolchain", "html": "bin/x build produces ludicc, the compiler you'll use for everything below." }, { "title": "Compile & run an example", "html": "bin/x app examples/games/snake.ludic turns a .ludic file into a native binary. Run it to open a real window." }, { "title": "Go headless for tests", "html": "--headless renders frames to a .ppm from piped input — deterministic output you can diff." }, { "title": "Edit with full tooling", "html": "bin/x tools builds the formatter and language server; every editor gets completion, diagnostics and go-to-definition." } ], "terminal_name": "zsh — ludic", "terminal": [ { "comment": "build the toolchain (once)" }, { "cmd": "bin/x build" }, { "blank": true }, { "comment": "build and run an example (opens a window)" }, { "cmd": "bin/x app examples/games/snake.ludic" }, { "cmd": "./build/snake" }, { "blank": true }, { "comment": "deterministic headless render for tests" }, { "cmd": "bin/x app examples/games/snake.ludic --headless" }, { "cmd": "printf 'ddddwww' | ./build/snake_headless" }, { "out": "→ writes 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": "bin/x tools builds ludic-fmt and ludic-lsp — the same formatter runs as a CLI for pre-commit hooks and CI." } }