This document describes the signals.json file used by BeamNG levels for traffic signals.
signals.json stores traffic signal data such as stop signs, traffic lights, signal controllers, and timed signal sequences. It is used by traffic AI, navigation, signal visualization, and gameplay systems.
The default traffic signals file is located in the level root:
levels/<levelName>/signals.json
Example:
levels/west_coast_usa/signals.json
If the file exists, it is automatically loaded when the level starts.
| File | Purpose |
|---|---|
signals.json |
Level signal instances, controllers, and sequences. |
signalControllerDefinitions.json |
Optional level-specific custom controller state/type definitions. |
settings/trafficSignals.json |
Default global controller state/type definitions. |
map.json / navgraph |
Used to associate signals with road graph nodes. |
Traffic signals are split into three main data types:
| Type | Description |
|---|---|
| Instance | A placed signal point in the world. Has position, direction, controller, and optional sequence. |
| Controller | Defines what type of signal this is and which states it can use. |
| Sequence | Defines timed phases for one or more controllers, usually for traffic light intersections. |
These are stored in three top-level arrays:
{
"instances": [],
"controllers": [],
"sequences": {}
}
or, for modern saved files:
{
"instances": [],
"controllers": [],
"sequences": []
}
sequences ({}). New files should use an array ([]).A simple stop sign setup can use one controller and many instances.
{
"controllers": [
{
"id": 2,
"name": "stop",
"type": "signStop",
"isSimple": true,
"states": [
{
"state": "basicStop"
}
]
}
],
"instances": [
{
"id": 1,
"name": "stop 1",
"controllerId": 2,
"sequenceId": 0,
"pos": [734.165, 755.369, 177.949],
"dir": [-0.925, 0.378, -0.005],
"group": "group_1_3",
"startDisabled": false
}
],
"sequences": []
}
This creates one stop sign instance using the shared stop controller.
Signals use numeric IDs to link elements together.
| Field | Description |
|---|---|
instance.id |
Unique ID for a signal instance. |
controller.id |
Unique ID for a controller. |
sequence.id |
Unique ID for a sequence. |
instance.controllerId |
References a controller ID. |
instance.sequenceId |
References a sequence ID. 0 means no sequence/basic behavior. |
IDs must be unique across all instances, controllers, and sequences.
Signal instances represent placed signals in the world.
Example:
{
"id": 1,
"name": "stop 1",
"controllerId": 2,
"sequenceId": 0,
"pos": [734.165, 755.369, 177.949],
"dir": [-0.925, 0.378, -0.005],
"group": "group_1_3",
"startDisabled": false
}
| Field | Type | Description |
|---|---|---|
id |
number | Unique signal instance ID. |
name |
string | Signal instance name. Also used to link world signal objects. |
pos |
array[3] | World position of the signal point. |
dir |
array[3] | Direction the signal faces / applies to. |
group |
string | Optional intersection/group name. |
controllerId |
number | Controller used by this instance. Required for functional signals. |
sequenceId |
number | Sequence used by this instance. 0 means no sequence/basic signal. |
useCurrentLane |
bool | Optional/experimental lane-specific behavior. Currently unused. |
startDisabled |
bool | If true, signal starts inactive/off. |
pos defines where the signal logic point is placed.
"pos": [734.165, 755.369, 177.949]
dir defines the direction the signal applies toward.
"dir": [-0.925, 0.378, -0.005]
For a stop sign or traffic light, dir should generally point in the direction vehicles travel when approaching or passing the signal.
The signal system uses pos and dir to:
The optional group field groups related signals, usually at the same intersection.
Example:
"group": "group_8_9_10_11"
Groups are mostly editor/organization metadata. The editor can automatically group instances that are close together, share the same sequence, and face toward the same intersection area.
Controllers define what kind of signal behavior an instance uses.
Example stop sign controller:
{
"id": 2,
"name": "stop",
"type": "signStop",
"isSimple": true,
"states": [
{
"state": "basicStop"
}
]
}
| Field | Type | Description |
|---|---|---|
id |
number | Unique controller ID. |
name |
string | Controller name. |
type |
string | Controller type key from controller definitions. |
isSimple |
bool | If true, controller does not need timed sequence logic. |
defaultIndex |
number | Optional default state index. |
states |
array | Ordered list of controller states. |
Each controller contains an ordered list of states:
"states": [
{
"state": "greenTrafficLight",
"duration": 12
},
{
"state": "yellowTrafficLight",
"duration": 4
},
{
"state": "redTrafficLight",
"duration": 1
}
]
| Field | Type | Description |
|---|---|---|
state |
string | State key from controller definitions. |
duration |
number | Duration of this state in seconds. Optional for simple controllers. |
A negative duration is treated as an infinite duration in editor/runtime logic.
Simple controllers do not require a sequence.
Example:
{
"id": 2,
"name": "stop",
"type": "signStop",
"isSimple": true,
"states": [
{
"state": "basicStop"
}
]
}
Signal instances using simple controllers can use:
"sequenceId": 0
This is typical for:
Sequences define timed signal phases.
They are mainly used for traffic lights at intersections.
Example:
{
"id": 10,
"name": "intersection_sequence",
"startTime": 0,
"startDisabled": false,
"ignoreTimer": false,
"phases": [
{
"controllerIds": [3]
},
{
"controllerIds": [4]
}
]
}
| Field | Type | Description |
|---|---|---|
id |
number | Unique sequence ID. |
name |
string | Sequence name. |
phases |
array | Ordered list of phases. |
startTime |
number | Start offset in seconds. Can be negative to skip ahead. |
startDisabled |
bool | If true, sequence starts disabled/off. |
ignoreTimer |
bool | If true, sequence does not advance with the main timer. |
A phase defines which controllers run together.
{
"controllerIds": [3, 4]
}
Each controller can only be used once per sequence.
A sequence can contain multiple phases:
"phases": [
{ "controllerIds": [3] },
{ "controllerIds": [4] }
]
The duration of a phase is calculated from the longest controller duration in that phase.
A phase may optionally define startTime:
{
"startTime": 5,
"controllerIds": [3]
}
This allows phases to overlap or start at explicit offsets inside the sequence timeline.
If no startTime is set, phases run one after another.
The controller type and state names are resolved from controller definition data.
Default definitions are loaded from:
settings/trafficSignals.json
A level can override or extend them with:
levels/<levelName>/signalControllerDefinitions.json
When loading a level, the signal system:
signalControllerDefinitions.json in the level folder.states and types into the defaults.signals.json.This optional file defines custom signal states and controller types for a level.
Simplified example:
{
"states": {
"customFlashingYellow": {
"name": "Custom Flashing Yellow",
"action": "slow",
"duration": 3,
"lights": ["yellow"],
"flashingLights": [
["yellow"],
["black"]
],
"flashingInterval": 0.5
}
},
"types": {
"customWarningLight": {
"name": "Custom Warning Light",
"states": ["customFlashingYellow"],
"defaultIndex": 1,
"isSimple": true
}
}
}
| Field | Type | Description |
|---|---|---|
name |
string | Display name for the state. |
action |
string | AI/navigation action associated with the state. |
duration |
number | Default duration in seconds. |
lights |
array[string] | Visible light colors for this state. |
flashingLights |
array[array[string]] | Optional sequence of light color arrays for flashing behavior. |
flashingInterval |
number | Time between flashing light changes. |
| Field | Type | Description |
|---|---|---|
name |
string | Display name for the controller type. |
states |
array[string] | Ordered list of state keys used by this type. |
defaultIndex |
number | Default state index. |
isSimple |
bool | Whether this type can be used without a sequence. |
Signal states can define an action. This action is sent to vehicle AI through the navigation signal data.
Known action names include:
| Action | Meaning |
|---|---|
alert |
Alert/caution behavior. |
stop |
Stop behavior. |
briefStop |
Short stop behavior. |
slow |
Slow/caution behavior. |
none |
No special action. |
Internally these are converted to numeric action values and sent to vehicle Lua.
Signal states define visible light colors using strings.
Common colors:
red
amber
yellow
green
cyan
blue
white
black
black means off.
Example:
"lights": ["red", "black", "black"]
Traffic signal objects use instance color fields:
instanceColor
instanceColor1
instanceColor2
instanceColor3
The signal system switches these values on/off depending on the current state.
A signal instance is logical data. The visible 3D traffic light/sign objects are separate scene objects.
Visible signal objects are usually TSStatic objects with a dynamic field:
signalInstance
The value must match the signal instance name.
Example:
signalInstance = "traffic light 1"
When the signal changes state, all linked objects with that signalInstance value are updated.
When the level starts, the signal system:
signals.json.The signal system depends on the navigation graph. If the navgraph is reloaded, traffic signals are finalized again.
At runtime, signals are associated with road graph edges.
For each signal instance, the system determines the best road segment and stores data such as:
This data is sent to vehicle Lua so AI vehicles can react to stop signs, traffic lights, and other signals.
Use:
World Editor → Gameplay → Traffic Signals Editor
The editor provides tabs for:
It can:
signals.jsonsignals.json.TSStatic signal objects.{
"controllers": [
{
"id": 1,
"name": "stop",
"type": "signStop",
"isSimple": true,
"states": [
{
"state": "basicStop"
}
]
}
],
"instances": [
{
"id": 2,
"name": "stop 1",
"controllerId": 1,
"sequenceId": 0,
"pos": [0, 0, 0],
"dir": [1, 0, 0],
"startDisabled": false
}
],
"sequences": []
}
{
"controllers": [
{
"id": 1,
"name": "north_south",
"type": "lightsBasic",
"states": [
{"state": "greenTrafficLight", "duration": 12},
{"state": "yellowTrafficLight", "duration": 4},
{"state": "redTrafficLight", "duration": 1}
]
},
{
"id": 2,
"name": "east_west",
"type": "lightsBasic",
"states": [
{"state": "greenTrafficLight", "duration": 12},
{"state": "yellowTrafficLight", "duration": 4},
{"state": "redTrafficLight", "duration": 1}
]
}
],
"sequences": [
{
"id": 3,
"name": "intersection_sequence",
"startTime": 0,
"phases": [
{"controllerIds": [1]},
{"controllerIds": [2]}
]
}
],
"instances": [
{
"id": 4,
"name": "traffic light north",
"controllerId": 1,
"sequenceId": 3,
"pos": [0, 10, 0],
"dir": [0, -1, 0],
"group": "intersection_01",
"startDisabled": false
},
{
"id": 5,
"name": "traffic light east",
"controllerId": 2,
"sequenceId": 3,
"pos": [10, 0, 0],
"dir": [-1, 0, 0],
"group": "intersection_01",
"startDisabled": false
}
]
}
dir point along the vehicle approach/pass direction.signalInstance.Possible causes:
controllerId is missing or invalid.sequenceId is invalid.Possible causes:
TSStatic object.signalInstance dynamic field.signalInstance value does not match the signal name.Possible causes:
Possible causes:
ignoreTimer is true.Stop sign/simple signal controllers usually do not need a sequence. Use:
"sequenceId": 0
Check:
signalControllerDefinitions.json
and verify that the controller type exists.
signals.json defines level traffic signal logic.
It contains:
instances - placed signal pointscontrollers - signal behavior/state listssequences - timed phase logicThe system links signal instances to nearby navigation graph edges, updates visible signal objects through signalInstance, and sends signal actions to vehicle AI.
Was this article helpful?