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.
Top-level schema
Section titled “Top-level schema”{ "grid_width": <int>, "grid_height": <int>, "objects": { "<name>": { ... }, "<name>": { ... } }}| Field | Type | Description |
|---|---|---|
grid_width | integer | Width of the world grid in voxels. |
grid_height | integer | Height of the world grid in voxels. |
objects | object | Named world objects. Keys are object names; values are per-object records. |
Per-object structure
Section titled “Per-object structure”Each object in objects contains three parallel arrays and one adjacency map.
{ "indices": [<int>, ...], "types": [<int>, ...], "neighbors": { "<index>": [<int>, ...], ... }}| Field | Type | Description |
|---|---|---|
indices | int[] | Flat grid indices of every non-empty voxel, sorted ascending. |
types | int[] | 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).
Coordinate system
Section titled “Coordinate system”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 + xAnd the inverse:
x = flat_index % grid_widthy = flat_index // grid_widthImportant: 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.
Cell type values
Section titled “Cell type values”See Voxel Types for the complete reference. The values encoded in types are:
| Value | Constant | Notes |
|---|---|---|
| 0 | EMPTY | Not written to JSON — only non-empty cells appear in indices. |
| 1 | RIGID | Dynamic structural material. |
| 2 | SOFT | Dynamic deformable material. |
| 3 | H_ACT | Horizontal actuator. |
| 4 | V_ACT | Vertical actuator. |
| 5 | FIXED | Static, anchored (ground, walls, platforms). |
| 6 | CONTRACTILE | Universal per-axis actuator. |
| 7–30 | SLOPE_*, 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.
Worked example
Section titled “Worked example”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 3Flat indices:
- Ground voxels: y=0 → indices 0, 1, 2, 3
- Robot
H_ACTat (x=1, y=1): index = 1×4 + 1 = 5 - Robot
V_ACTat (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.
Generating and consuming JSON
Section titled “Generating and consuming JSON”Python API
Section titled “Python API”import numpy as npfrom 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)
# Writeworld.to_json("my_world.json")
# Readloaded = EvoWorld.from_json("my_world.json")
# Get the dict without writing to diskd = world.to_json_dict()Designer import / export
Section titled “Designer import / export”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.
Validation rules
Section titled “Validation rules”The Python loader (add_from_json) enforces:
- Every voxel index in
indicesmust also be a key inneighbors. indicesandtypesmust have equal length.- 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. - No two objects may share a grid cell. Overlapping objects raise
ValueError. - Neighbor references must point to flat indices that appear in the same object’s
indiceslist.