Skip to content

Automation & Batch / Headless Operation

Hesiod can run without its GUI. Headless operation is useful for:

  • Automation pipelines — generate terrain assets as part of a build or CI step.
  • Large renders — queue overnight jobs at resolutions that would be impractical to wait on interactively.
  • Seed sweeps and procedural variation — produce many outputs from one graph by varying parameters in a script loop.

Two tools cover this: the built-in batch mode bundled with the Hesiod binary, and the hsd toolkit — a Python package in this repository that compiles compact JSON specs into valid .hsd files and drives the binary programmatically.


Opening a project from the command line

Independently of batch mode, the binary accepts a project file to open in the GUI — useful for shell aliases, file-manager associations, and scripted workflows that end with a visual check:

hesiod myproject.hsd
hesiod -f myproject.hsd
hesiod --file myproject.hsd

The three forms are equivalent; -f/--file is easier to keep unambiguous in scripts that pass several flags. The example-selector dialog is skipped and the file behaves as a regularly opened project (Ctrl+S saves back to it, and it is added to the File → Open Recent menu).

  • If the positional argument and -f/--file are both given with different values, Hesiod prints an error explaining the conflict and exits with a non-zero status.
  • If the file does not exist, a warning is logged and startup continues as if no file had been given.

The Open Recent list is stored in the settings JSON (~/.config/hesiod/hesiod.json on Linux) under global.recent_files; its length is capped by global.max_recent_files (default 10).


Built-in batch mode

The Hesiod binary accepts a --batch flag that loads a .hsd graph file and renders it without opening the GUI.

hesiod --batch=graph.hsd [--shape=W,H] [--tiling=X,Y] [--overlap=R]

Flags:

Flag Description
--batch=<file> Path to the .hsd graph to execute. Required to enter batch mode.
--shape=W,H Override the heightmap shape in pixels, e.g. --shape=2048,2048.
--tiling=X,Y Override tiling, e.g. --tiling=4,4 to split into 16 tiles.
--overlap=R Override the tile overlap ratio (0–1), e.g. --overlap=0.25.

Output: export happens only if the graph defines an export path (export_param — see Export configuration below). When an export path is configured, the batch run writes:

  • <path>.png — 16-bit grayscale heightmap.
  • <path>_preview.png — TERRAIN colourmap + hillshade composite.

If the graph has no export path set, the graph is computed but no files are written.

Other modes (mutually exclusive with --batch):

Flag Description
--inventory Print a JSON inventory of every node type the binary knows about.
--snapshot Generate node snapshots (used internally for documentation).

Headless display

To suppress the Qt display (required on servers without a display):

QT_QPA_PLATFORM=offscreen hesiod --batch=graph.hsd --shape=1024,1024

Running from the build directory

The binary must be run from a directory that contains the data/ folder (node docs, colour gradients, etc.):

cd /path/to/Hesiod
QT_QPA_PLATFORM=offscreen build/bin/hesiod --batch=my_graph.hsd

Export configuration

The export behaviour in batch mode is controlled by the export_param block stored inside the .hsd file. It holds:

Field Description
export_path Output file path (without extension); the binary appends .png and _preview.png.
ids Which node/port outputs to export.
shape Exported PNG resolution, baked in at build time from the spec's config.shape. To change export dimensions, update config.shape in the spec and rebuild.
tiling Default tile grid. Overridden by --tiling.
overlap Default overlap ratio. Overridden by --overlap.

To configure these fields from the GUI, use the Bake & Export dialog (Alt+E, or File → Bake & Export). Full details on what the dialog controls and the resulting export directory structure are in Bake and Export.


The hsd toolkit

The hsd toolkit is a Python package (scripts/hsd/) that lets you author .hsd graphs programmatically — without the GUI and without manually editing the XML-like .hsd format. You describe a graph in a compact JSON spec, validate it, and either build a .hsd or build-and-run in one step.

Invoke from the repo root with PYTHONPATH=scripts:

PYTHONPATH=scripts python3 -m hsd <subcommand> [args]

Subcommands

Subcommand Purpose
nodes --search T Search the node catalogue by keyword.
nodes --category C List nodes in a category.
nodes --show TYPE Show ports and parameters for a node type.
build SPEC -o OUT Compile a JSON spec to a .hsd file (no render).
validate SPEC Check a spec for type errors, unknown nodes, bad links. Prints ok or structured errors.
lint FILE Model↔UI consistency check on a compiled .hsd file (not a spec JSON).
run FILE [--shape --tiling --overlap] Run a pre-built .hsd with the Hesiod binary.
make SPEC -o OUT [--run ...] Compile + optionally run in one step. The most common command.

For rendering (run or make --run), set:

export HESIOD_BIN=/path/to/hesiod
export QT_QPA_PLATFORM=offscreen

Spec format

A spec is a JSON object with four top-level keys:

{
  "config": {"shape": [W, H], "tiling": [X, Y], "overlap": R},
  "nodes":  [{"id": "...", "type": "...", "params": {...}}],
  "links":  [["fromNode.fromPort", "toNode.toPort"]],
  "export": [{"node": "...", "port": "...", "path": "out.png"}]
}
  • config — optional; defaults to shape:[1024,1024], tiling:[1,1], overlap:0.0.
  • nodes — each entry needs id (arbitrary string), type (exact node type name), and optionally params (only the values you want to override — omitted params use Hesiod's defaults).
  • links — each entry is ["source.port", "dest.port"] using the id strings defined in nodes.
  • export — required for file output. Without it the graph runs but writes nothing.

Port data-type rule

Every link must connect ports of the same data type. The two types that matter for terrain work are:

Type Meaning Typical nodes
VirtualArray Heightmap / scalar field NoiseFbm, HydraulicParticle, ExportHeightmap
VirtualTexture RGBA colour image ColorizeGradient, ColorizeSolid, ExportTexture

These are incompatible — linking VirtualArray → VirtualTexture or vice versa produces a validation error. The primary bridge node is ColorizeGradient, which accepts a VirtualArray on its level input and emits a VirtualTexture on its texture output. ColorizeSolid also outputs a VirtualTexture, but it renders a uniform colour (set via its color param) and has no level input; it accepts an optional alpha (VirtualArray) input only.

Exporting both heightmap and colour requires a fork at the VirtualArray output:

NoiseFbm.output ──► HydraulicParticle.input
HydraulicParticle.output ──► ExportHeightmap.input       (VirtualArray path)
HydraulicParticle.output ──► ColorizeGradient.level
ColorizeGradient.texture ──► ExportTexture.texture       (VirtualTexture path)

Always run hsd nodes --show TYPE before wiring a node — port names are not always input/output. Confirmed port names for the export nodes:

  • ExportHeightmap: input input (VirtualArray), no output port.
  • ExportTexture: input texture (VirtualTexture), no output port.
  • ColorizeGradient: input level (VirtualArray), output texture (VirtualTexture).

Worked example

The verified spec at bridges/Claude/.claude/skills/hesiod-generate/reference/specs/heightmap_export.json:

{
  "config": {"shape": [1024, 1024], "tiling": [1, 1], "overlap": 0.0},
  "nodes": [
    {"id": "noise", "type": "NoiseFbm", "params": {"kw": [4, 4], "seed": 1}},
    {"id": "ero",   "type": "HydraulicParticle"},
    {"id": "exp",   "type": "ExportHeightmap"}
  ],
  "links": [
    ["noise.output", "ero.input"],
    ["ero.output",   "exp.input"]
  ],
  "export": [{"node": "ero", "port": "output", "path": "heightmap.png"}]
}

Validate, then build and run at a small test resolution:

# validate first — prints "ok" or structured errors
PYTHONPATH=scripts python3 -m hsd validate \
  bridges/Claude/.claude/skills/hesiod-generate/reference/specs/heightmap_export.json

# compile + render at 256×256 for a quick sanity check
HESIOD_BIN=/path/to/hesiod \
QT_QPA_PLATFORM=offscreen \
PYTHONPATH=scripts python3 -m hsd make \
  bridges/Claude/.claude/skills/hesiod-generate/reference/specs/heightmap_export.json \
  -o /tmp/test.hsd --run --shape 256,256 --tiling 1,1

On success the toolkit writes files at the path set in the spec's export[].path field (not derived from the -o .hsd path). For heightmap_export.json (export path "heightmap.png") that is, relative to cwd:

  • heightmap.png — 16-bit grayscale heightmap
  • heightmap_preview.png — TERRAIN colourmap + hillshade

To change the output file dimensions, update config.shape in the spec and rebuild — the exported PNG resolution is baked into the .hsd at build time; --shape overrides the compute config only. A verified 4096 × 4096 tiled spec (4 × 4 tiles, 0.25 overlap) is at bridges/Claude/.claude/skills/hesiod-generate/reference/specs/tiled_large.json.


See also

  • LLM-driven procedural generation — using the hsd toolkit with an LLM for seed sweeps, biome exploration, and large-map pipelines.
  • The hesiod-generate Claude Code skill lives at bridges/Claude/.claude/skills/hesiod-generate/SKILL.md — load it when you want Claude to author and validate specs for you.
  • Bake and Export — the GUI counterpart to batch mode; covers export directory structure and variants.
  • Tiling and Overlap — how tile boundaries are blended.