← back DynOSTiC Jan 23, 2026After working on Kaíven and ëëntïï, I realized I needed an engine. What I was doing with those two projects wasn’t good enough, something was missing.
After a few back-and-forths, I remembered an idea from another story: DYNamics Of the Space TIme Continuum. Reusing the name just felt right. In that story, the machine lets you travel through time and space, and it can even hange your DNA. I could keep going, but that’s probably for another time.
Anyway, I present DYNOSTIC.
Using Dynostic: A Practical Guide to the Deterministic Tactical-Sim Core
Dynostic is a reusable deterministic tactical-sim core in Rust with a LÖVE (LuaJIT) front-end. The design goal is fast iteration on UI/feel in LÖVE while keeping the simulation pure, deterministic, and event-sourced in Rust. This post focuses on how to get it running and how to use the CLI and packaging pipeline day to day.
What you are running
rust/contains the Cargo workspace for the sim core, FFI boundary, and CLI.love/is the LÖVE project that loads the Rust library and renders the UI.- Data is pack-driven, so you can create and ship content without changing engine code.
Prerequisites
- Rust toolchain (stable) with Cargo
- LÖVE (LuaJIT build)
makefor the convenience targets (optional)ffmpegonly if you want offline video encoding
Quick start: build and run
From the repo root:
This builds the dynostic_ffi dynamic library and copies it into:
love/native/<platform>/
Then run the LÖVE project:
You should see a small window with a moving dot driven by the Rust sim. If the native library is missing or mismatched, the LÖVE app will fail to load the FFI.
Create a new game scaffold
Generate a pack skeleton with the CLI:
Explore the reference game for data layout and conventions:
examples/reference_game
Packs are data-driven, so you can iterate on JSON content without touching the engine. This is the fastest path to new units, abilities, and scenarios.
Profile the simulation
The CLI can run headless and dump hot-path counters:
Use this when tuning pathfinding, line-of-sight, or ability targeting rules.
Work with mods (experimental)
Mods live under mods/ and are enabled via mods/mods.json. Use the CLI to add
and toggle them:
When a mod is enabled, its pack is merged after the base pack. Script
permissions are gated by pack.permissions.scripts (default false).
Speed up loads with pack cache
Compile pack data into a deterministic cache blob:
The runtime uses pack.cache.json automatically when present. Disable with
DYNOSTIC_PACK_CACHE=0 if you need to test cold loads.
Cutscenes and previews
Validate and run a deterministic cutscene timeline:
For the LÖVE preview tool, launch the app and press F7. Use DYNOSTIC_CINE
to load a different timeline.
Package and ship
Build a portable .love archive and a platform folder with native libs:
For reproducible builds, set SOURCE_DATE_EPOCH:
SOURCE_DATE_EPOCH=1700000000
To build a release manifest and verify it:
Dynostic can sign packs and release manifests with an ed25519 key:
Note: dynamic libraries cannot be loaded from inside a zipped .love archive.
Ship platform builds with the native library next to the executable/app bundle.
Optional: offline replay rendering
To render deterministic replays into PNG frames (or video with ffmpeg):
DYNOSTIC_RENDER=1 DYNOSTIC_RENDER_REPLAY=../tools/golden/replay_combat.json \
DYNOSTIC_RENDER=1 DYNOSTIC_RENDER_REPLAY=../tools/golden/replay_combat.json \
DYNOSTIC_RENDER_FFMPEG=ffmpeg
This is useful for capture pipelines, trailers, and visual regression testing.