Skip to content

World JSON Format

Every world in jax-evogym is serialisable to and from a JSON file. The format is stable across the Python API, the web designer, and the original C++ EvoGym. EvoWorld.to_json and EvoWorld.from_json are the canonical read/write paths.

{
"grid_width": <int>,
"grid_height": <int>,
"objects": {
"<name>": { ... },
"<name>": { ... }
}
}
FieldTypeDescription
grid_widthintegerWidth of the world grid in voxels.
grid_heightintegerHeight of the world grid in voxels.
objectsobjectNamed world objects. Keys are object names; values are per-object records.

Each object in objects contains three parallel arrays and one adjacency map.

{
"indices": [<int>, ...],
"types": [<int>, ...],
"neighbors": {
"<index>": [<int>, ...],
...
}
}
FieldTypeDescription
indicesint[]Flat grid indices of every non-empty voxel, sorted ascending.
typesint[]Cell type for each voxel, in the same order as indices.
neighbors{string: int[]}Adjacency map: key is a flat index (as a string), value is the list of flat indices of its directly connected neighbours, sorted ascending.

indices and types are always the same length. Every index that appears in indices must also appear as a key in neighbors (even if its neighbour list is empty, e.g. an isolated voxel).

The grid uses a y-up convention. Row 0 is the bottom of the world; row grid_height - 1 is the top.

A voxel at grid position (x, y) — where x counts from the left and y counts from the bottom — maps to the flat index:

flat_index = y * grid_width + x

And the inverse:

x = flat_index % grid_width
y = flat_index // grid_width

Important: EvoWorld.add_from_array accepts arrays in user convention (row 0 = top, matching how you write NumPy arrays visually). The JSON stores voxels in internal convention (row 0 = bottom). The conversion (numpy.flipud) happens automatically on load and save — you never need to flip manually when working through the Python API.

See Voxel Types for the complete reference. The values encoded in types are:

ValueConstantNotes
0EMPTYNot written to JSON — only non-empty cells appear in indices.
1RIGIDDynamic structural material.
2SOFTDynamic deformable material.
3H_ACTHorizontal actuator.
4V_ACTVertical actuator.
5FIXEDStatic, anchored (ground, walls, platforms).
6CONTRACTILEUniversal per-axis actuator.
7–30SLOPE_*, SLOPE2_*, SLOPE3_*Legacy unsupported slope terrain values. Loadable for inspection; rejected by stable compilation unless allow_experimental_slopes=True.

allow_experimental_slopes=True enables geometry-only inspection of legacy slope cells. It does not enable slope friction, slope grip, or supported shallow-ramp physics.

A minimal two-object world: a 4-cell fixed ground strip and a 2×1 robot with one horizontal actuator and one vertical actuator, placed on top at positions (1, 1) and (2, 1).

Grid layout (W=4, H=2, y-up):

row 1: . H V . (y=1: robot at x=1,2)
row 0: F F F F (y=0: ground at x=0,1,2,3)
0 1 2 3

Flat indices:

  • Ground voxels: y=0 → indices 0, 1, 2, 3
  • Robot H_ACT at (x=1, y=1): index = 1×4 + 1 = 5
  • Robot V_ACT at (x=2, y=1): index = 1×4 + 2 = 6
{
"grid_width": 4,
"grid_height": 2,
"objects": {
"ground": {
"indices": [0, 1, 2, 3],
"types": [5, 5, 5, 5],
"neighbors": {
"0": [1],
"1": [0, 2],
"2": [1, 3],
"3": [2]
}
},
"robot": {
"indices": [5, 6],
"types": [3, 4],
"neighbors": {
"5": [6],
"6": [5]
}
}
}
}

The neighbors map encodes spring connectivity between voxels. Adjacent voxels that share an edge are listed as neighbours. The simulator uses this to determine which voxels share spring forces across object boundaries.

import numpy as np
from jax_evogym import EvoWorld, FIXED, H_ACT, V_ACT
world = EvoWorld()
world.add_from_array("ground", np.array([[FIXED, FIXED, FIXED, FIXED]]), x=0, y=0)
world.add_from_array("robot", np.array([[H_ACT, V_ACT]]), x=1, y=1)
# Write
world.to_json("my_world.json")
# Read
loaded = EvoWorld.from_json("my_world.json")
# Get the dict without writing to disk
d = world.to_json_dict()

The web designer reads and writes this exact format. Use File → Import to load a JSON file and File → Export to download the current canvas as JSON. The coordinate system conversion is handled automatically: the designer displays y-up (row 0 at the bottom), matching the JSON convention.

When you export from the designer and load the result with EvoWorld.from_json, the object positions and voxel types will be identical to what you see on the canvas.

The Python loader (add_from_json) enforces:

  1. Every voxel index in indices must also be a key in neighbors.
  2. indices and types must have equal length.
  3. All type values must be known constants (0–30). Unknown values raise ValueError. Values 7–30 are legacy slope ids and are not stable simulation ids.
  4. No two objects may share a grid cell. Overlapping objects raise ValueError.
  5. Neighbor references must point to flat indices that appear in the same object’s indices list.