🎮 Controller Configuration Format

This document describes the structure and semantics of the configuration system used to map game controllers (e.g., joysticks, gamepads) to controls in Train Sim World and Train Simulator Classic. It is designed to be flexible, extensible, and friendly to both analog and digital input devices.


📦 Overview

Each control on a game controller can be assigned an action. Assignments describe when and how the actions are triggered based on the input. Actions describe what happens when triggered.

All assignments conform to a top-level enum ControllerProfileControlAssignment, which contains the following variants:

TypeDescription
MomentarySingle-press controls that trigger on threshold crossing
ToggleOn/off state controls that alternate between actions
LinearMulti-threshold controls for fine-grained behavior
DirectControlDirect value mapping to the game
SyncControlSynchronized control via keypresses
ApiControlHTTP API-based value mapping
VirtualActionVirtual controls actions

Each assignment type has a specific use case and behavior, described below.


🧩 Assignment Types

🔘 Momentary

Used for buttons that act while held.

{
  "type": "momentary",
  "threshold": 0.9,
  "match": "exceeds",
  "action_activate": { ... },
  "action_deactivate": { ... }
}

Properties

PropertyDescriptionRequired
thresholdThe threshold value that triggers the action✅ Yes
matchHow to interpret the threshold. Defaults to "exceeds" where the action is executed when the value exceeds the threshold. Can also be set to "equals" for an exact comparisonNo
action_activateThe action to execute when the threshold is exceeded✅ Yes
action_deactivateThe action to execute when the threshold is no longer exceeded. Defaults to releasing the previously activated key(s)No
conditionsOptional conditions that must be met for the assignment to executeNo
rail_class_informationOptional list of rail classes this assignment applies toNo

Examples

Simple Key Press
{
  "type": "momentary",
  "threshold": 0.9,
  "action_activate": {
    "keys": "h"
  }
}
Key Press with Timing
{
  "type": "momentary",
  "threshold": 0.9,
  "action_activate": {
    "keys": "w",
    "press_time": 0.2,
    "wait_time": 0.2
  }
}
Direct Control with API Fallback
{
  "type": "momentary",
  "threshold": 0.9,
  "action_activate": {
    "controls": "Throttle_{SIDE}",
    "value": 1.0,
    "hold": true,
    "enable_api_fallback": true
  },
  "action_deactivate": {
    "controls": "Throttle_{SIDE}",
    "value": 0.0,
    "enable_api_fallback": true
  }
}
Conditional Momentary

Momentary assignments can be conditioned on other control values:

{
  "type": "momentary",
  "threshold": 0.9,
  "conditions": [
    {
      "control": "mylever",
      "operator": "gte",
      "value": 0.5
    }
  ],
  "action_activate": {
    "keys": "h"
  }
}

In the above example, the assignment will only execute if mylever exceeds 0.5.

Supported Action Types

Momentary assignments support the following action types:

🔁 Toggle

Used for toggle switches that alternate between two states.

{
  "type": "toggle",
  "threshold": 0.9,
  "match": "exceeds",
  "action_activate": { ... },
  "action_deactivate": { ... }
}

Properties

PropertyDescriptionRequired
thresholdThe threshold value that triggers the toggle action✅ Yes
matchHow to interpret the threshold. Defaults to "exceeds" where the action is executed when the value exceeds the threshold. Can also be set to "equals" for an exact comparisonNo
action_activateThe action to execute on the first toggle (when transitioning from off to on)✅ Yes
action_deactivateThe action to execute on the second toggle (when transitioning from on to off)✅ Yes
conditionsOptional conditions that must be met for the assignment to executeNo
rail_class_informationOptional list of rail classes this assignment applies toNo

Toggle State Machine

The Toggle assignment maintains internal state to track whether it has been activated. The behavior follows this logic:

  1. Initial State (No prior call): When the threshold is exceeded, action_activate is executed.
  2. Toggled State (Previous call was above threshold): When the threshold is exceeded again, action_deactivate is executed.
  3. Below Threshold: When the input falls below the threshold:
    • If the previous action was a key press, it is released.
    • If the previous action was a direct control or API control, no action is taken (the value is not automatically reverted).

Examples

Simple Key Toggle
{
  "type": "toggle",
  "threshold": 0.9,
  "action_activate": {
    "keys": "x"
  },
  "action_deactivate": {
    "keys": "shift+x"
  }
}

Pressing the button once triggers x, pressing again triggers shift+x.

Direct Control Toggle
{
  "type": "toggle",
  "threshold": 0.9,
  "action_activate": {
    "controls": "MarkerLight_R",
    "value": 1
  },
  "action_deactivate": {
    "controls": "MarkerLight_R",
    "value": 0.5
  }
}

Pressing the button once sets the marker light to 1, pressing again sets it to 0.5.

Toggle with Timing
{
  "type": "toggle",
  "threshold": 0.9,
  "action_activate": {
    "keys": "w",
    "press_time": 0.2,
    "wait_time": 0.2
  },
  "action_deactivate": {
    "keys": "s",
    "press_time": 0.2,
    "wait_time": 0.2
  }
}
Conditional Toggle

Toggle assignments can be conditioned on other control values:

{
  "type": "toggle",
  "threshold": 0.9,
  "conditions": [
    {
      "control": "mylever",
      "operator": "gte",
      "value": 0.5
    }
  ],
  "action_activate": {
    "keys": "h"
  },
  "action_deactivate": {
    "keys": "shift+h"
  }
}

In the above example, the toggle will only execute if mylever exceeds 0.5.

Toggle with Equals Match
{
  "type": "toggle",
  "threshold": 0.5,
  "match": "equals",
  "action_activate": {
    "keys": "a"
  },
  "action_deactivate": {
    "keys": "b"
  }
}

This toggle only triggers when the input value exactly equals 0.5.

Supported Action Types

Toggle assignments support the following action types:

📈 Linear

Used for analog levers or sliders with multiple threshold points.

{
  "type": "linear",
  "neutral": 0.5,
  "thresholds": [
    { "value": 0.2, "action_activate": { ... } },
    { "value": 0.5, "action_activate": { ... } },
    { "value": 0.8, "action_activate": { ... } }
  ]
}

Properties

PropertyDescriptionRequired
thresholdsArray of threshold definitions with actions✅ Yes
neutralOptional neutral/idle value for value normalization (e.g., 0.5 for centering 0-1 range to -1 to 1)No
conditionsOptional conditions that must be met for the assignment to executeNo
rail_class_informationOptional list of rail classes this assignment applies toNo

Threshold Properties

PropertyDescriptionRequired
valueThe threshold value to exceed. Can use named references from calibration✅ Yes
value_endEnd value for auto-generating thresholds (exclusive)No
value_stepStep increment for auto-generating thresholds between value and value_endNo
action_activateThe action to execute when the threshold is exceeded✅ Yes
action_deactivateThe action to execute when the value falls below the thresholdNo

Auto-Generation of Thresholds

When value_end and value_step are provided, the system automatically generates multiple thresholds between value and value_end:

{
  "type": "linear",
  "thresholds": [
    {
      "value": 0.3,
      "value_end": 0.6,
      "value_step": 0.05,
      "action_activate": { "keys": "w" }
    }
  ]
}

This generates thresholds at: 0.3, 0.35, 0.40, 0.45, 0.50, 0.55 (all triggering the same action).

Neutral Value Mapping

The neutral property allows you to map the input value range around a neutral point. This is useful for levers that have a centered neutral position:

{
  "type": "linear",
  "neutral": 0.5,
  "thresholds": [
    { "value": -0.5, "action_activate": { "keys": "s" } },
    { "value": 0.5, "action_activate": { "keys": "w" } }
  ]
}

With neutral: 0.5, a raw input of 0.0 becomes -1.0 (neutralized), and 1.0 becomes 1.0.

Threshold Exceeding Logic

The Linear assignment uses asymmetric threshold logic (signifying applying power as opposed to mathmatical operations):

State Machine Behavior

Linear assignments track which thresholds are currently exceeding and which were previously passed:

  1. Activation: When new thresholds start exceeding (that weren't previously passed), their action_activate is triggered.
  2. Deactivation: When thresholds stop exceeding (were passed but no longer are), their action_deactivate is triggered if defined.
  3. Key release: If action_deactivate is not defined but action_activate uses keys, the keys are released.
  4. Clear state: If neither deactivation nor key release is possible, the previous call is cleared to allow re-triggering.

Examples

Simple Key Press at Thresholds
{
  "type": "linear",
  "thresholds": [
    { "value": 0.2, "action_activate": { "keys": "a" } },
    { "value": 0.5, "action_activate": { "keys": "d" } },
    { "value": 0.8, "action_activate": { "keys": "f" } }
  ]
}

As the lever moves from 0 to 1:

Brake Lever with Neutral Position
{
  "type": "linear",
  "neutral": 0.5,
  "thresholds": [
    { "value": -0.5, "action_activate": { "keys": "s" } },
    { "value": 0.5, "action_activate": { "keys": "w" } }
  ]
}

With a neutral value of 0.5, the lever's centered position (0.5 raw) is treated as 0 (neutralized), allowing negative and positive threshold values.

Auto-Generated Thresholds for Notched Control
{
  "type": "linear",
  "thresholds": [
    {
      "value": 0.0,
      "value_end": 0.5,
      "value_step": 0.1,
      "action_activate": { "keys": "1" }
    },
    {
      "value": 0.6,
      "value_end": 1.0,
      "value_step": 0.1,
      "action_activate": { "keys": "2" }
    }
  ]
}

This creates 6 thresholds for each range (0.0, 0.1, 0.2, 0.3, 0.4, 0.5 and 0.6, 0.7, 0.8, 0.9, 1.0), all triggering the same action within each range.

Conditional Linear Assignment

Linear assignments can be conditioned on other control values:

{
  "type": "linear",
  "conditions": [
    {
      "control": "mylever",
      "operator": "gte",
      "value": 0.5
    }
  ],
  "thresholds": [
    { "value": 0.2, "action_activate": { "keys": "a" } },
    { "value": 0.7, "action_activate": { "keys": "d" } }
  ]
}

In the above example, the assignment will only execute if mylever exceeds 0.5.

Supported Action Types

Linear assignments support the following action types:

🎚️ DirectControl

Maps an analog controller input to a continuous value in-game. This is the primary method for controlling cab levers and other continuous controls in Train Sim World, Train Simulator Classic and Wonders of Sodor.

{
  "type": "direct_control",
  "controls": "Throttle1",
  "input_value": {
    "min": 0.0,
    "max": 1.0,
    "invert": true
  },
  "notify": true
}

Properties

NameDescriptionRequired
controlsThe UE4SS control identifier to control (e.g., Throttle, AutomaticBrake, IndependentBrake)✅ Yes
input_valueDefines the input value constraints and mapping✅ Yes
control_rangeRemaps a partial input range to a full 0-1 or 0,-1 output rangeNo
holdWhether to continuously hold this value. Useful for levers which automatically reset (such as the Deadman or some brake levers)No
use_normalizedWhether to use normalized values (-1 to 1) instead of raw values (0 to 1) [rarely used]No
enable_api_fallbackWhether to enable fallback to the TSW API if direct control is unavailableNo
notifyWhether to enable the in-game notifier when changing values to display the current value (defaults to true)No
conditionsOptional conditions that must be met for the assignment to executeNo
rail_class_informationOptional list of rail classes this assignment applies toNo

input_value Properties

NameDescriptionRequired
minThe minimum reachable value in the game cab✅ Yes
maxThe maximum reachable value in the game cab✅ Yes
stepThe step increment to auto-generate discrete values (alternative to steps)No
stepsArray of discrete values. Can include null to create free range zones between detentsNo
invertWhether to reverse the input value direction (mapping 0-1 to 1-0)No
max_change_rateThe maximum rate at which the control value can change per frame (rarely necessary)No
step_thresholdsCustom threshold definitions for each step (see step_thresholds)No
Step Thresholds - Custom Thresholds for Stepped Controls

The step_thresholds option allows you to define custom threshold values for each step in a direct control mapping. This is particularly useful when you want to remap a continuous analog input to match a notched control (e.g., a throttle with detents) or when you need to create custom value ranges.

{
  "type": "direct_control",
  "controls": "Throttle1",
  "input_value": {
    "min": 0.1,
    "max": 1.0,
    "steps": [0.1, null, 1.0],
    "step_thresholds": [
      { "threshold": 0.2, "threshold_tolerance": 0.05 },
      { "threshold": 0.5, "threshold_end": 0.6, "threshold_tolerance": 0.03 },
      { "threshold": 0.8 }
    ]
  }
}

Threshold Properties:

PropertyDescriptionRequired
thresholdThe actual threshold value where the step begins (can use named references from calibration)✅ Yes
threshold_endThe end value for range-based thresholds (optional)No
threshold_toleranceThe tolerance around the threshold (optional)No

How It Works:

  1. Single Threshold: When only threshold is specified, it defines a single point value. The control will snap to this value when the input matches the threshold (+- the tolerance).

  2. Range Threshold: When both threshold and threshold_end are specified, it defines a range. The control will accept any input value within this range and map it proportionally. This is mostly useful for free range steps.

  3. Tolerance: The threshold_tolerance defines how much deviation from the threshold is acceptable. For example, if threshold is 0.5 and threshold_tolerance is 0.05, the control will accept input values between 0.45 and 0.55.

  4. Default Tolerance: If no tolerance is specified, a default tolerance is calculated based on the number of steps (approximately half the step size).

  5. Free Range Zones: When a step is marked as a free range zone (using null in the steps array), it gets special handling with no tolerance by default. The threshold defines the boundaries of the free range.

Note: The number of step_thresholds should match the number of steps as each step threshold definition corresponds to each step.

Use Cases:

Control Range - Remapping Partial Ranges

The control_range property allows you to remap a partial input range to a full 0-1 or 0,-1 output range. This can be useful for mapping a single physical lever or control to multiple in-game controls while retaining a full 0-1 direct control range.

{
  "type": "direct_control",
  "controls": "Throttle1",
  "input_value": {
    "min": 0.0,
    "max": 1.0
  },
  "control_range": {
    "start": 0.2,
    "end": 0.8
  }
}

In the above example, an input value of 0.2 maps to 0.0, 0.8 maps to 1.0, and values outside this range are clamped. This effectively remaps the 0.2-0.8 input range to the full 0-1 output range.

Control Range Properties:

PropertyDescriptionRequired
startThe start value of the input range to remap (depending on value direction, this is the min or max)✅ Yes
endThe end value of the input range to remap✅ Yes
Steps with Free Range Zones

The steps array can include null values to create free range zones between detents. This is useful for controls that are partly notched and partly free.

{
  "type": "direct_control",
  "controls": "AutomaticBrake1",
  "input_value": {
    "min": 0,
    "max": 0.8,
    "steps": [0, 0.125, null, 0.6, 0.7, 0.8]
  }
}

In the above example:

Conditional Direct Control Assignments

Direct Control assignments can be conditioned on other control values, allowing the same physical control to map to different game controls based on conditions.

{
  "type": "direct_control",
  "conditions": [
    {
      "control": "mylever",
      "operator": "lt",
      "value": 0.5
    }
  ],
  "controls": "IndependentBrake",
  "input_value": {
    "min": 0.25,
    "max": 1,
    "invert": true
  }
}

In the above example, the IndependentBrake is only controlled when the Reverser (mylever) is less than 0.5. When the Reverser exceeds 0.5, a separate Direct Control assignment would map to DynamicBrake.

Examples

Basic Throttle Mapping
{
  "type": "direct_control",
  "controls": "Throttle_{SIDE}",
  "input_value": {
    "min": 0,
    "max": 1
  }
}
Brake with Discrete Steps
{
  "type": "direct_control",
  "controls": "AutomaticBrake_{SIDE}",
  "input_value": {
    "min": 0,
    "max": 0.8,
    "steps": [0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.8]
  }
}

🧭 SyncControl [DEPRECATED]

An alternative to DirectControl for locomotives that don't work with direct control. Unlike DirectControl which directly sets values, SyncControl uses a state machine approach that reads the current in-game state and uses keypresses to incrementally reach the desired target value.

Note: this control mode is generally not recommended anymore since there is no reason to use it in favor of direct control or API control.

How It Works:

  1. The profile runner reads the current in-game control value via the TSW connector event sync_control_value
  2. It compares the current value to the target value derived from your controller input
  3. If the values differ beyond a margin of error (0.005), it triggers keypresses (action_increase or action_decrease) to move toward the target
  4. The Moving state tracks direction: -1 (decreasing), 0 (idle), 1 (increasing)
  5. When the current value reaches the target (within margin of error), keypresses are released
{
  "type": "sync_control",
  "identifier": "Reverser1",
  "input_value": {
    "min": -1.0,
    "max": 1.0,
    "steps": [-1.0, 0.0, 1.0]
  },
  "action_increase": { "keys": "PageUp" },
  "action_decrease": { "keys": "PageDown" }
}

Properties

NameDescriptionRequired
typeMust be "sync_control"✅ Yes
identifierThe sync control identifier to control; can be identified using the Cab Debugger✅ Yes
input_valueDefines the input value constraints and mapping (same as DirectControl)✅ Yes
action_increaseThe key action to press when increasing the control value✅ Yes
action_decreaseThe key action to press when decreasing the control value✅ Yes
control_rangeRemaps a partial input range to a full 0-1 or 0,-1 output rangeNo
conditionsOptional conditions that must be met for the assignment to executeNo
rail_class_informationOptional list of rail classes this assignment applies toNo

input_value Properties

SyncControl uses the same input_value properties as DirectControl:

NameDescriptionRequired
minThe minimum reachable value in the game cab✅ Yes
maxThe maximum reachable value in the game cab✅ Yes
stepThe step increment to auto-generate discrete valuesNo
stepsArray of discrete values. Can include null to create free range zones between detentsNo
invertWhether to reverse the input value directionNo
max_change_rateThe maximum rate at which the control value can change per frameNo
step_thresholdsCustom threshold definitions for each step (see Step Thresholds)No

State Machine Behavior

SyncControl maintains an internal state machine for each controlled identifier:

Moving States:

Margin of Error: The system uses a margin of error of 0.005 to determine when to stop moving. This prevents micro-adjustments and ensures smooth stopping at target values.

State Transitions:

  1. Start Increasing: When target > current and abs(target - current) > 0.005 and not already moving
  2. Start Decreasing: When target < current and abs(target - current) > 0.005 and not already moving
  3. Stop Moving: When the control reaches or exceeds the target value (within margin of error)

🎚️ ApiControl

Maps an analog controller input to a continuous value in-game using the HTTP API. Runs at 15fps processing loop.

{
  "type": "api_control",
  "controls": "Throttle1",
  "input_value": {
    "min": 0.0,
    "max": 1.0
  }
}

Properties

PropertyDescriptionRequired
controlsThe direct/api control name to control; can be identified using the Cab Debugger✅ Yes
input_valueDefines the input value constraints✅ Yes
control_rangeRemaps partial input ranges to full 0-1 or 0,-1 output rangesNo
holdBoolean to keep the control active after input is releasedNo
input_value Properties
PropertyDescriptionRequired
minThe minimum reachable value in the game cab✅ Yes
maxThe maximum reachable value in the game cab✅ Yes
max_change_rateThe maximum rate at which this control can change (useful for realistic throttle simulation)No
step / stepsQuantize values to discrete steps or define free range zonesNo
invertWhether to invert the input value before calculating the game valueNo
control_range Properties
PropertyDescriptionRequired
startThe starting value of the partial range✅ Yes
endThe ending value of the partial range✅ Yes

Processing Behavior:

Example with ControlRange:

{
  "type": "api_control",
  "controls": "Throttle1",
  "input_value": {
    "min": 0.0,
    "max": 1.0
  },
  "control_range": {
    "start": 0.3,
    "end": 0.7
  }
}

Maps the partial range 0.3-0.7 to full 0-1 output range

Example with MaxChangeRate:

{
  "type": "api_control",
  "controls": "Throttle1",
  "input_value": {
    "min": 0.0,
    "max": 1.0,
    "max_change_rate": 0.1
  }
}

Throttle can only change by 0.1 per frame, required for certain locomotives / controls - but rare

Example with Hold:

{
  "type": "api_control",
  "controls": "Brake1",
  "input_value": {
    "min": 0.0,
    "max": 1.0
  },
  "hold": true
}

Brake maintains applied state after button release


⚙️ Action Types

Each assignment triggers an action when activated (and optionally when deactivated). Actions can be:

🖱️ Key Presses

{
  "keys": "W",
  "press_time": 0.1,
  "wait_time": 0.05
}

🎛️ Direct Control Action

{
  "controls": "Throttle1",
  "value": 0.5,
  "hold": false,
  "relative": false
}

Direct Control Action Properties

PropertyDescriptionRequired
controlsThe UE4SS control identifier to control✅ Yes
valueThe value to send to the control✅ Yes
max_change_rateThe maximum rate at which the control value can change per frameNo
relativeWhether to use the value as a relative adjustment instead of absoluteNo
holdWhether to continuously hold the value by sending it repeatedlyNo
use_normalizedWhether to use normalized values instead of raw values (rarely used)No
notifyWhether to enable the in-game notifier when changing valuesNo
enable_api_fallbackWhether to enable fallback to the TSW API if direct control is unavailableNo

🎛️ Api Control Action

{
  "controls": "Throttle1",
  "api_value": 0.5
}

🎛️ Virtual Action

{
  "type": "virtual",
  "control": "virtual:MyVirtualControl",
  "value": 0.1
}

🔧 Input Value Mapping

Used by DirectControl, SyncControl, and ApiControl to map axis input to control values.

{
  "min": -1.0,
  "max": 1.0,
  "step": 0.1,
  "steps": [0.0, 0.2, null, 0.5, null, 1.0],
  "invert": true,
  "max_change_rate": 0.05
}

📏 Step Thresholds

The step_thresholds option allows you to define custom threshold values for each step in a direct control mapping. This is particularly useful when you want to remap a continuous analog input to match a notched control (e.g., a throttle with detents) or when you need to create custom value ranges.

{
  "type": "direct_control",
  "controls": "Throttle1",
  "input_value": {
    "min": -1.0,
    "max": 1.0,
    "steps": [0.1, null, 1.0],
    "step_thresholds": [
      { "threshold": 0.2, "threshold_tolerance": 0.05 },
      { "threshold": 0.5, "threshold_end": 0.6, "threshold_tolerance": 0.03 },
      { "threshold": 0.8 }
    ]
  }
}

Threshold Properties

PropertyDescriptionRequired
thresholdThe actual threshold value where the step begins (can use named references from calibration)✅ Yes
threshold_endThe end value for range-based thresholds (optional)No
threshold_toleranceThe tolerance around the threshold (optional)No

How It Works

  1. Single Threshold: When only threshold is specified, it defines a single point value. The control will snap to this value when the input matches the threshold (+- the tolerance).

  2. Range Threshold: When both threshold and threshold_end are specified, it defines a range. The control will accept any input value within this range and map it proportionally. This is mostly useful for free range steps.

  3. Tolerance: The threshold_tolerance defines how much deviation from the threshold is acceptable. For example, if threshold is 0.5 and threshold_tolerance is 0.05, the control will accept input values between 0.45 and 0.55.

  4. Default Tolerance: If no tolerance is specified, a default tolerance is calculated based on the number of steps (approximately half the step size).

  5. Free Range Zones: When a step is marked as a free range zone (using null in the steps array), it gets special handling with no tolerance by default. The threshold defines the boundaries of the free range.

Note: It is important that the number of step_thresholds matches the number of steps as each step threshold definition corresponds to each step.

Use Cases


🔁 Conditional Assignments

It is also possible to only execute assignments depending on one or more conditions. This can be used to create multi-key assignments (e.g., the action of a button changes depending on the position of a lever).

This can be added to any assignment using the conditions key:

{
  "type": "momentary",
  "conditions": [
    {
      "control": "mylever",
      "operator": "gte",
      "value": 0.5
    }
  ]
}

In the above example, the assignment will only execute if mylever exceeds 0.5. At this time the supported operators are eq,gte, lte, gt, and lt.


🏗️ Profile Structure

A controller profile is defined in a JSON file with the following structure:

{
  "name": "MyProfile",
  "extends": "BaseProfile",
  "auto_select": true,
  "controls": [
    {
      "name": "Button1",
      "type": "momentary",
      "threshold": 0.5,
      "action_activate": { "keys": "H" },
      "action_deactivate": { "keys": "Shift+H" }
    }
  ],
  "controller": {
    "usb_id": "0x1234",
    "mapping": "Standard",
    "calibration": { ... }
  },
  "rail_class_information": [
    "Class 40",
    "Class 42",
    "Class 43"
  ]
}

Root Properties

PropertyDescriptionRequired
nameProfile name✅ Yes
extendsProfile to extend fromNo
auto_selectAuto-detection supportNo
controlsArray of control definitionsNo
controllerController-specific infoNo
rail_class_informationSupported rail classesNo

Controller Section

The controller section contains controller-specific information. It is optional and is mostly available for profile sharing.

"controller": {
  "usb_id": "...",
  "mapping": { ... },
  "calibration": { ... }
}

Rail Class Information

The rail_class_information section is an array of supported rail class names:

"rail_class_information": [{ "class_name": "..." }]

✅ Best Practices


Happy simming! 🚂