Menu ▾ ▴

Map format (.json)

Rufus Pearce

Fio levels are stored as plain .json files. The level format is the editor's native scene representation and is consumed directly by the engine.

A Fio level contains:

  • version — format version
  • brushes — world geometry
  • things — entities and gameplay objects
  • terrain_data — optional procedural terrain configuration
  • lightmap_scene_hash — optional lightmap bake identifier
  • logic_graph — optional saved logic-graph node positions

Root Structure

A minimal Fio V3 level is:

{
    "version": 3,
    "brushes": [],
    "things": []
}

Additional sections are written only when the corresponding level data exists.


version

"version": 3

The current level format version is 3 (July 2026)

The loader treats a missing version as version 1 for backwards compatibility and performs legacy migration when necessary.


brushes

brushes is an array containing the level's world geometry.

The normal brush representation is an axis-aligned box:

{
    "pos": [0.0, 64.0, 0.0],
    "size": [256.0, 128.0, 256.0],
    "textures": {
        "north": "default.png",
        "south": "default.png",
        "east": "default.png",
        "west": "default.png",
        "top": "default.png",
        "down": "default.png"
    },
    "is_trigger": false,
    "id": "..."
}

Brush fields

Field Type Description
pos array Brush centre [x, y, z]
size array Brush dimensions [x, y, z]
textures object Texture assigned to each face
is_trigger boolean Marks the brush as a trigger volume
id string Stable unique identifier
uv_scale object Optional per-face UV scaling
geometry object Optional convex geometry for angled/clipped brushes
name string Optional brush name
lock boolean Optional editor lock state
hidden boolean Optional visibility state
shader string Optional brush shader
is_door boolean Optional door behaviour
is_mover boolean Optional moving-brush behaviour
lightmap_static boolean Optional participation in lightmap baking

Not every brush contains every optional field.


Brush Textures

The textures object assigns a texture path to the six standard faces:

"textures": {
    "north": "default.png",
    "south": "default.png",
    "east": "default.png",
    "west": "default.png",
    "top": "default.png",
    "down": "default.png"
}

Texture paths are stored as project-relative asset paths.

For example:

"textures": {
    "north": "Dev/512.jpg",
    "south": "Dev/512.jpg",
    "east": "Dev/512.jpg",
    "west": "Dev/512.jpg",
    "top": "Dev/512.jpg",
    "down": "Dev/512.jpg"
}

uv_scale

uv_scale optionally stores independent UV scaling for individual faces.

"uv_scale": {
    "north": [1.625, 0.125],
    "south": [1.625, 0.125],
    "east": [1.5625, 0.125],
    "west": [1.5625, 0.125],
    "top": [1.625, 1.5625],
    "down": [1.625, 1.5625]
}

The value is [u_scale, v_scale].

A brush may contain an empty uv_scale object or omit it entirely.


Angled Brushes

V3 supports brushes whose geometry is no longer represented solely by pos and size.

An angled or clipped brush can contain a geometry object:

"geometry": {
    "planes": [
        {
            "n": [1.0, 0.0, 0.0],
            "d": 768.0,
            "texture": "default.png",
            "face": "east"
        },
        {
            "n": [-1.0, 0.0, 0.0],
            "d": -432.0,
            "texture": "default.png",
            "face": "west"
        },
        {
            "n": [0.0, 1.0, 0.0],
            "d": 208.0,
            "texture": "default.png",
            "face": "top"
        }
    ]
}

Each plane contains:

Field Description
n Plane normal [x, y, z]
d Plane distance/offset
texture Texture assigned to the plane
face Optional association with a standard brush face

The plane representation allows the editor to retain arbitrary convex brush geometry created by clipping and rotation.


Brush Runtime Data

The renderer and geometry systems attach temporary internal data to brushes while the application is running.

These runtime caches are deliberately removed when the level is serialized. This prevents OpenGL/GLM/NumPy objects and other non-JSON data from being written into map files.

Therefore, a saved .json file represents the persistent brush state rather than the renderer's runtime caches.


things

things contains all placeable entities in the level.

The common structure is:

{
    "type": "light",
    "pos": [128.0, 96.0, 0.0],
    "properties": {},
    "io_connections": []
}

Entity fields

Field Type Description
type string Entity type used when loading the object
pos array World position [x, y, z]
properties object Entity-specific persistent properties
io_connections array Output/input connections

Every normal Thing has a stable UUID stored in its properties:

"properties": {
    "type": "light",
    "name": "Light_1",
    "id": "53e6fc4d-aef0-4109-98cf-7695c304ee85"
}

Entity Properties

properties is deliberately type-dependent.

The entity class determines which properties are available.

For example, a light may contain:

"properties": {
    "type": "light",
    "name": "Light_1",
    "colour": [255, 255, 255],
    "intensity": 1.0,
    "radius": 512.0,
    "state": "on",
    "show_radius": false,
    "casts_shadows": false,
    "id": "..."
}

A player start contains different properties:

"properties": {
    "type": "playerstart",
    "name": "PlayerStart_1",
    "angle": 0.0,
    "id": "..."
}

The format therefore does not impose one fixed property schema on every entity.


Stable Entity IDs

V3 uses persistent UUIDs to identify brushes and entities.

For Things, the ID is stored inside properties:

"id": "e107abe6-ff24-4ac6-bf74-264baeb3e5bf"

The ID survives save/load and allows systems such as I/O and Big World to refer to the same object independently of its position in the things array.


I/O Connections

I/O connections are stored separately from the entity's ordinary properties.

Example:

"io_connections": [
    {
        "output": "OnTrigger",
        "target": "main_door",
        "input": "Open",
        "parameter": "",
        "delay": 0.0,
        "fire_once": false,
        "target_id": "..."
    }
]

Connection fields

Field Description
output Output event emitted by the source
target Target entity name
input Input called on the target
parameter Optional parameter passed to the input
delay Delay before the connection fires
fire_once Whether the connection may fire only once
target_id Stable UUID of the target entity

io_connections is always emitted for Things, including when the array is empty.

Brush I/O is also serialized into io_connections when a brush has I/O connections.


Terrain

Procedural terrain is optional and is stored at the root level as terrain_data.

A terrain-enabled level therefore has:

"terrain_data": {
    "enabled": true,
    "solid": true,
    "seed": 42,
    ...
}

The terrain data stores the procedural terrain configuration rather than a baked mesh.

The configuration includes the terrain's biome and generation parameters, including values such as:

  • base height
  • height scale
  • hills scale and intensity
  • mountain generation settings
  • plateau generation settings
  • valley generation settings
  • biome/texturing information
  • terrain height sampling scale

For example, current maps contain values such as:

"terrain_data": {
    "enabled": true,
    "solid": true,
    "seed": 42,
    "name": "Low Poly Valley",
    "base_height": 10.0,
    "height_scale": 250.0,
    "hills_scale": 0.004,
    "hills_intensity": 0.8
}

Terrain is independent of the brush list; a level can contain brushes without having terrain_data at all.


Lightmap Scene Hash

When a lightmap bake has produced a scene hash, it may be stored at the root:

"lightmap_scene_hash": "..."

The hash identifies the scene state associated with the saved lightmap data.

When a map is loaded, the saved hash is restored into the lightmap bake state. A newly loaded scene is still treated as requiring verification/rebaking before its first Play session.


Logic Graph

The visual logic graph can persist node positions independently of the underlying I/O connections.

When graph positions exist, the root contains:

"logic_graph": {
    "node_positions": {
        "entity-uuid": {
            "x": 320.0,
            "y": 180.0
        }
    }
}

The key is the entity's stable ID.

The positions are editor layout data; they do not define the actual I/O behaviour of the level.


Coordinates

Fio uses Cartesian 3D world coordinates.

X = horizontal
Y = vertical
Z = depth

Positions and dimensions are stored as numeric JSON values.


Serialization

The editor serializes the live EditorState directly into the level dictionary before writing the .json file.

The normal save operation is effectively:

json.dump(self.state.get_level_data(), f, indent=4)

The serialized level therefore contains persistent scene data rather than a separate compiled or baked level representation.

Runtime-only renderer and geometry cache fields are stripped before serialization.


Loading and Compatibility

The loader accepts older map versions.

If version is absent, the map is treated as V1.

V1 maps can be migrated automatically, including legacy target properties used by the earlier I/O system.

V2 and later maps use the separate io_connections representation.

Missing brush IDs are also backfilled with UUIDs when older data is loaded.


Minimal V3 Map

{
    "version": 3,
    "brushes": [
        {
            "pos": [0.0, 64.0, 0.0],
            "size": [256.0, 128.0, 256.0],
            "textures": {
                "north": "default.png",
                "south": "default.png",
                "east": "default.png",
                "west": "default.png",
                "top": "default.png",
                "down": "default.png"
            },
            "is_trigger": false,
            "id": "00000000-0000-0000-0000-000000000001"
        }
    ],
    "things": [
        {
            "type": "playerstart",
            "pos": [0.0, 128.0, 0.0],
            "properties": {
                "type": "playerstart",
                "name": "PlayerStart_1",
                "angle": 0.0,
                "id": "00000000-0000-0000-0000-000000000002"
            },
            "io_connections": []
        }
    ]
}

Version History

V1

Original level format.

  • No separate io_connections representation
  • Legacy target properties were used for entity/brush communication
  • Entity IDs were not part of the original format

V2

Introduced the modern I/O connection representation and entity IDs.

V3

Current format.

  • version: 3
  • Stable IDs for scene objects
  • Persistent logic-graph node positions
  • Optional terrain_data
  • Optional lightmap_scene_hash
  • Convex/angled brush geometry
  • Per-face uv_scale
  • Additional brush rendering and gameplay properties such as shader, lightmap_static, doors, movers and triggers
  • Continued backwards compatibility with earlier map versions