Worlds from the ground up.
Deterministic world generation in Rust.
Terrain, climate, and water, from a single seed.
Generate a continent, resolve its terrain and drainage into local areas, and save it as binary world data. Export PNG maps and JSON, or load the cells and water features through the Rust library. Run locally or in Docker.
Procedural 64 × 64-square tactical maps from Arda 1.0 (MICRO seed 42; the snow peak from the same seed at --latitude 58,72), served by arda-server with an AI-generated art library: city, port town, village, hamlet; forest, woodland, mountain, snow peak; wetland, coast, dungeon, cave.
Install · Quick start · What you get · Exports · Library · Docker and servers · Status · Development
Explore the architecture reference.
Tagged releases carry prebuilt arda and arda-server binaries for Linux (x86_64, aarch64), macOS (Apple silicon) and Windows (x86_64), and a multi-architecture image. Download an archive and SHA256SUMS from the releases page, then check it:
sha256sum --check --ignore-missing SHA256SUMS
tar -xzf arda-<version>-x86_64-unknown-linux-gnu.tar.gzOr pull the image (placeholder tactical art library only; <version> is a release number such as 1.0.0):
docker pull ghcr.io/dicemasterio/arda:<version>Or build from source with stable Rust and a native C toolchain (compiler and linker):
git clone https://github.com/DiceMasterIO/arda.git
cd arda
cargo install --path crates/arda-cli --lockedThis installs the arda command. To build inside the checkout instead:
cargo build --release -p arda-cli --lockedThe binary is written to target/release/arda (arda.exe on Windows).
Use release builds for generation.
Start with the eight-area MICRO world:
# Generate the saved world data.
arda generate --seed 42 --micro --out worlds/w42
# Render an overview from that world.
arda export --world worlds/w42 --overview --out exports/w42
# Export one area at the default 8K resolution and as JSON.
arda export --world worlds/w42 --area 1,1 --out exports/w42
arda export --world worlds/w42 --area 1,1 --format json --out exports/w42Generation writes world.json and the native binary layers under
worlds/w42/. The exports are overview.png, area_01_01.png, and
area_01_01.json. Area coordinates are zero-based x,y pairs.
Generation needs a new or empty destination. Exporting reads the saved world; it does not rerun terrain or water generation.
For a single command that generates a new world and renders its overview:
arda preview --seed 42 --micro --quality 2k --out previews/w42This writes previews/w42/world/ and previews/w42/overview.png.
preview runs generation too, so use export for a world you already have.
Arda currently concentrates on the physical world: relief, climate, drainage, rivers, and lakes. It produces saved data as well as images.
| Layer | What it contains |
|---|---|
| Continent | Coarse relief shaped by tectonics, plus climate and drainage on a 1 km grid |
| Areas | 512 × 512 cells at 100 m spacing: 51.2 × 51.2 km per area, with terrain, rainfall, temperature, and water data |
| Water features | Shared river reaches and lakes with IDs, geometry, water surfaces, and annual flow information |
| Tactical blocks | Experimental 64 × 64 grids of five-foot tiles, sampled at selected land cells; this layer is unfinished |
The default requested extent is 500 × 1000 km. --micro selects a
102 × 204 km request with eight exported areas. Custom extents use
--size WIDTHxHEIGHT:
arda generate --seed 42 --size 500x1000 --out worlds/continent42Requested kilometre extents and the exported area grid are not identical: area counts currently use integer 51 km units, while each area spans 51.2 km. See operations for the exact modeled bounds and resource limits.
Seed + configuration
↓
Continent relief and climate
↓
Shared terrain evolution across area boundaries
↓
Connected drainage and annual water balance
↓
Saved areas, water features, and sampled blocks
↓
PNG maps · JSON exports · Rust queries
Terrain comes before water. Fine relief follows local regional height differences. Erosion evolves across internal area boundaries, so those boundaries do not become fixed terrain walls.
Lakes need a basin and a water supply. The water model combines physical depressions with rainfall, runoff, and evaporation. River and lake records are shared across areas, rather than reconstructed separately by each map export.
The saved world is the source for exports. PNG and JSON exports read persisted terrain and water geometry. Increasing image resolution changes the rendering, not the underlying terrain samples.
Generation is reproducible. A fixed seed, configuration, and generator revision determine the output. CI checks a shared golden world on Linux, macOS, and Windows. Keep the generator revision with a seed when you need to reproduce an older world; changes to generation can change its output.
arda generate automatically writes the native saved world. Its .bin
files carry terrain, climate, cells, and water records. No separate --format bin export is
needed.
worlds/w42/
├── world.json # Manifest, seed, configuration, format version
├── continent/
│ ├── overview.bin # Coarse terrain, climate, and drainage
│ └── objects.bin
├── areas/<ax>_<ay>/
│ ├── cells.bin # Full saved area cell records
│ └── objects.bin # Local objects and shared water context
├── hydrology/ # Global .bin tables: lakes, reaches, basins, ...
Worlds written before 1.0 also contain a blocks/ folder of sampled
tiles; nothing reads it any more and it may be deleted.
World::load opens this directory through the Rust API. Preserve the
directory structure when moving a world between a desktop, container,
or backend server.
| Output | Command options | Result |
|---|---|---|
| World overview | --overview |
overview.png, 8K long edge by default |
| Area map | --area 0,0 |
area_00_00.png, 8192 × 8192 by default |
| Small area preview | --area 0,0 --quality 512 |
area_00_00.png, 512 × 512 |
| High-resolution area | --area 0,0 --quality 32k |
area_00_00.png, 32768 × 32768 |
| Area data | --area 0,0 --format json |
area_00_00.json, versioned cell and object data |
Each row uses arda export --world WORLD_DIR ... --out EXPORT_DIR.
PNG and JSON are separate exports. --quality applies to area and overview
PNGs; it does not apply to JSON or tactical blocks.
Choose --style atlas for earthy elevation colours, directional relief shading
and sea-depth colours. Atlas interpolates terrain colour and lighting at every
output pixel while keeping the saved land/water masks and channel geometry.
Omitting --style, or choosing --style classic, retains the original render.
Explicit styles apply only to area/overview PNGs, not JSON or tactical blocks.
arda export --world worlds/w42 --overview --style atlas --quality 8k --out exports/w42-atlas
arda export --world worlds/w42 --area 1,1 --style atlas --quality 32k --out exports/w42-atlasQuality accepts any whole-pixel size from 512 through 32768, including
512, 1k, 2k, 4k, 8k, 16k, and 32k. A k is 1024 pixels.
The default is 8K. Areas are square; overviews preserve the exported
area grid's aspect ratio and use the selected quality for the long edge.
Detailed area maps still use the same 100 m terrain grid. Their additional pixels make channel geometry easier to inspect and smooth Atlas colour/lighting transitions; they do not add finer terrain measurements. Overview river strokes are symbolic and should not be read as physical channel widths.
Area JSON includes selected terrain and water fields for every cell, plus water objects. Temperature and rainfall are saved in the world and available through the Rust API, but are not yet included in area JSON. The saved world stores binary layers for loading and querying; JSON provides an explicit interchange format. A full area JSON can be tens of megabytes, so applications should choose the areas and fields they need. Browser loading and network delivery are not implemented or benchmarked here.
Choose a resolution up to 32K
Use the same quality option for a saved world or a single area:
arda export --world worlds/w42 --overview --quality 32k --out exports/w42-32k
arda export --world worlds/w42 --area 1,1 --quality 16k --out exports/w42-16kBoth paths stream PNG output using rows or bands, instead of keeping the whole uncompressed image in memory. Larger exports still require more rendering time and output space. Completed images replace the previous file only after rendering succeeds.
For compatibility, Classic --detail retains the older 4096 × 4096 area
export and its _detail.png filename. With --style atlas, --detail uses
the streamed 4096 × 4096 path and the standard area_XX_YY.png filename.
It cannot be combined with --quality. The preview command also retains
--px for pixels per area; it conflicts with --quality and any explicit
--style. Atlas preview uses --style atlas --quality SIZE.
The arda crate is the public entry point for generation, loading,
queries, and exports. Within a checkout, another workspace crate can use
arda = { path = "../arda" }.
use arda::{GenerateConfig, World};
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let path = Path::new("worlds/library42");
arda::generate(42, GenerateConfig::MICRO, path)?;
let world = World::load(path)?;
let area = world.read_area(0, 0)?;
let cell = area.cell(100, 100)?;
println!("Elevation: {} mm", cell.height.raw());
Ok(())
}World::load reads the manifest without eagerly loading every area.
area() caches requested areas; read_area() returns an owned, uncached
read. generate_with_limits() accepts explicit resource limits and
rejects work that exceeds them, without changing the physical model to
fit.
Generate local API documentation with:
cargo doc --workspace --no-deps --openArda can generate and export worlds on a server using the same CLI or
Rust API. The Docker image packages that CLI (arda) and the HTTP server
(arda-server). To run the CLI, build locally, then mount a writable world
directory and select the CLI entry:
docker build -t arda-local .
docker run --rm -v /absolute/path/to/worlds:/worlds --entrypoint arda arda-local \
generate --seed 42 --micro --out /worlds/w42
docker run --rm -v /absolute/path/to/worlds:/worlds --entrypoint arda arda-local \
export --world /worlds/w42 --area 1,1 --format json --out /worlds/exports/w42The container runs as UID 10001; the mounted directory must be writable by
that user. Native .bin layers and exported files remain
on the mounted volume after the container exits.
The CLI is also the entry for one-off jobs (--entrypoint arda, above); the HTTP server is
arda-server.
The same image runs arda-server, the read-only HTTP service, as its default entry. Mount a
generated world read-only; the server never writes to it:
docker build -t arda:local .
docker run --rm -p 8787:8787 -v /absolute/path/to/worlds/w42:/worlds/world:ro arda:local
curl http://127.0.0.1:8787/v1/healthWith no arguments the entry serves /worlds/world on 0.0.0.0:8787. Pass arda-server flags
to override that (they replace the defaults, so give --world and --host 0.0.0.0 yourself), or
set ARDA_LIBRARY / ARDA_WORLD. The image has a HEALTHCHECK (arda-server --probe), exposes
8787, stops on SIGTERM, and runs the CLI with --entrypoint arda:
docker run --rm --entrypoint arda arda:local --versionTwo variants build from one Dockerfile:
- Default (placeholder library only).
docker build -t arda:local .This is the variant to publish: the committed placeholder tactical library is Apache-2.0. - With the AI art library (local only). The generated library is not in git and its assets
are AI-generated (
LicenseRef-AI-generated), so the public image does not include it. Build it yourself with a named build context:docker build -t arda:local-ai --build-context artlib=/abs/path/to/out/art-gen/lib .It is stacked over the placeholder. Omittingartlibnever breaks the build.
tools/docker-smoke.sh <world> [--ai <lib>] builds and checks either variant end to end.
Working physical-world generator, with further world content in development.
Status updated on 2026-09-22 against source commit 342d03e55120.
| Implemented | What is available now |
|---|---|
| Physical geography | Continental relief and climate; shared 100 m terrain evolution; connected drainage, rivers, lakes and representative annual water accounting |
| Area data | Elevation, temperature, rainfall, slope, drainage, channel properties and wetness, plus basic Bare/Grass/Marsh ground cover |
| Terrain corrections | Erosion across area boundaries and the regional-relief correction that removed the recorded repeated lake-district artifact |
| Persistence and loading | Format-4 saved worlds, manifest-first loading, requested area/archive caches and bounded owned area reads for exports |
| Maps and interchange | Classic and shaded Atlas PNGs; per-output-pixel Atlas colour/light interpolation; versioned JSON; area/world image quality 512–32K, default 8K, with streamed quality PNG publication |
| Developer tools | generate, preview and export CLI commands, a reusable Rust API, Docker execution and regression/CI infrastructure |
| Tactical maps | On-demand 64×64-square blocks per 100 m cell from arda-refine/arda-blocks, served by arda-server |
| Remaining work or model boundary | Current boundary |
|---|---|
| Terrain realism | First Atlas comparison shows smoother relief; straight/parallel drainage shapes and 100 m shoreline steps remain visible. Wider terrain acceptance and historical rectangular-basin diagnosis remain open |
| Water model scope | Annual water is implemented; seasonal storage, groundwater, snowpack and dynamic floods are outside the current model |
| Rich vegetation and human geography | Full vegetation/forest density, soil moisture, settlements, roads, buildings, naming, realms and NPC generation remain deferred |
| Tactical maps | On-demand refined blocks; detailed assets and gameplay geometry remain deferred |
| Serving and integration | No arda serve command or browser application; consumers use saved exports or the Rust API |
The Atlas first pass passed 648 workspace tests on Linux (8 ignored), formatting, strict Clippy, Rust 1.96.1 checks and the dependency audit. Repeated 32K area/overview exports were byte-identical; saved world files were unchanged. Three independent comparisons against the pre-change executable preserved Classic PNG bytes. The visual panel uses one retained seed-42 200 × 300 km world; it does not complete the broader terrain-realism acceptance.
The earlier 32K export commit records 611 workspace tests and 74 targeted checks;
these are historical verification results, not a fresh test run. The last checked
CI run for base commit 23dfb0999672
passed Linux/macOS tests, lint, MSRV and dependency checks, but failed the
Windows golden-text comparison on LF versus CRLF line endings. Its 34 logged
fingerprint entries match after normalization; that test failure remains open.
The detailed status audit separates implemented
work, verification evidence and unresolved visual acceptance.
Generation is an offline batch. Valid size inputs can still exceed resource limits. Failures after generation begins can leave partial output for diagnosis; admission failures leave the destination untouched. There is no checkpoint resume; retry in a fresh directory. Incompatible saved-world format versions require regeneration.
See open items for the detailed status and operations for capacity and persistence behaviour.
The workspace separates the model, generation, rendering, public API, and CLI:
| Crate | Responsibility |
|---|---|
arda-core |
Coordinates, domain types, deterministic RNG, and storage formats |
arda-gen |
Continent, terrain, water, and area generation |
arda-render |
PNG rendering and JSON serialization |
arda |
Public generation, loading, query, and export API |
arda-cli |
The arda command |
Run the main local checks:
cargo fmt --all --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace --all-featuresCI also checks the golden world across three operating systems, the minimum supported Rust version, and dependency licenses/advisories. Generation tests do real work and can take substantially longer than ordinary unit tests.
Issues and pull requests are welcome. For generation or rendering bugs, include the seed, configuration, generator revision, exact command, and a map or data sample. Follow the coding standards and add a regression test for fixes. Golden fingerprints should change only when an intended generation change has been reviewed.
| Document | Use it for |
|---|---|
| Reference index | Navigating the implementation |
| Architecture | Crate boundaries and generation stages |
| Data models | Cells, water features, and saved formats |
| Operations | CLI behaviour, resource limits, and exports |
| Open items | Implemented work and remaining limitations |
| Testing | Determinism, physical checks, and verification scope |
Apache-2.0. See NOTICE for project attribution.