Scene JSON reference#
A scene file is a recipe for an experiment. It says where the equipment starts, how it moves, what counts as an event, and how the display describes those events. It does not contain the motion you have already recorded. Keep that distinction in mind when editing: Compile scene builds a fresh world from this recipe.
Use Scenes, agents, and the editor for the complete editor walkthrough and Task tutorials for the task tutorials. This page is the field-by-field companion: use it when you know what you want to change but need the exact spelling, units, limits, or interaction with another option. The defaults below are compiler defaults, not necessarily the values chosen by a shipped preset.
Compile a small, self-contained scene#
Open Edit scene, click Edit complete scene JSON, replace the text with this
example, and press Compile scene. Click the world after enabling Keyboard
control, then use W to accelerate, A/D to steer, S for reverse throttle,
and Space to brake. The first target is at [24, 12]; the second is at [8, 12].
After visiting both, the display counts one circuit and targets the first again.
The experiment goal is two gate events. Ordinary live driving continues after that
point; a batch experiment can stop successfully there.
The initial position is outside both gates. Two separated gates are deliberate: with one gate, a stationary vehicle inside it receives another gate event every frame. Gates detect occupancy of the next target, not a directional crossing of a finish line. They should also be separated enough that a vehicle cannot occupy successive targets without moving.
{
"version": 1,
"name": "Two-gate kart workshop",
"task": "navigation",
"size": [32, 24],
"bodies": [
{
"position": [14, 12],
"controlled": true,
"radius": 0.65,
"mass": 1,
"thrust": 12,
"drag": 0.7,
"actuator": {
"kind": "kart",
"wheelbase": 1,
"steering_limit": 0.6,
"lateral_grip": 14,
"yaw_response": 12,
"brake_deceleration": 18
},
"visual": {"model": "kart", "color": "#c69bd9", "scale": 1}
}
],
"gates": [
{"position": [24, 12], "radius": 2},
{"position": [8, 12], "radius": 2}
],
"presentation": {
"task_label": "Two-gate workshop",
"score": {"metric": "gates", "label": "Circuits", "divisor": 2},
"progress": {"metric": "gates", "label": "Next gate", "cycle": 2}
},
"evaluation": {"metric": "gates", "target": 2}
}
For the longer editor exercises, download the
foraging starter and
finished arena, the
cargo starter and
finished delivery course,
or the kart starter and
finished checkpoint course.
Use Import JSON ↑ to load them; use Export JSON ↓ to save your edited recipe.
World, geometry, and numerical settings#
Physical coordinates are always planar [x, y] vectors in metres. In ordinary
scenes these are simply the two axes of the map. In flight mode, the same two
coordinates are interpreted as horizontal travel x and altitude y; flight
mode changes the interpretation and presentation, not the dimension of the state
model. It may be selected explicitly or inferred automatically from the scene.
Angle zero points along positive x; positive angles rotate toward positive y. The
angled camera adds visual depth but does not add a third physical coordinate.
Flight mode starts with a side-on view, which makes altitude visible; use Side /
overhead to inspect the same motion overhead. Numbers are finite JSON numbers; booleans
are true and false, not strings. An omitted optional number uses the listed
default. Use omission rather than null when sharing files between the native
compiler and browser renderer.
Root field |
Type and default |
Meaning and limits |
|---|---|---|
|
Number, |
Only version 1 is supported. |
|
String, |
Human-readable scene name. |
|
Optional string |
Text shown beneath the environment controls; defaults to |
|
String, |
Presentation defaults and the special |
|
Two numbers, |
Arena extent in metres; each dimension is in |
|
Array of |
Outer playable polygon. Default corners are |
|
Array of polygon arrays, |
Excluded regions inside the boundary. |
|
Array of body objects; required |
Between 1 and 4096 bodies, including passive cargo. |
|
Object, |
Up to 256 named reusable body definitions. |
|
Arrays, each |
Cargo-body delivery, ordered checkpoint, food, and vehicle unloading zones. Pickups are limited to 4096 slots. |
|
Optional object; disabled when omitted |
Enables limited pickup storage on each controlled vehicle. Requires at least one refinery; distinct from body |
|
Arrays, each |
Force sources and body-to-body springs. |
|
Objects, |
Numerical integration and reward settings. |
|
Number, |
Default desired centre separation in metres for each controlled-body pair, |
|
Array, |
Optional pair-distance overrides for formation reward; each entry requires |
|
Number, |
Pickup cooldown in simulation seconds, |
|
Boolean, |
Retain delivered cargo. Asteroid harvesting and Collaborative mining both default to |
|
Optional objects |
Display and batch-experiment success settings. |
|
Optional objects |
Rendering, flight, and circuit-preview metadata. |
|
Optional Boolean; auto-detected when omitted |
|
|
|
Constant downward acceleration used when flight mode is active. |
|
Array, |
Definitions for native extensions already registered in the engine build. |
Polygon rules#
Write each ring as at least three non-collinear points, in either winding order. The compiler closes the ring and normalizes its winding. A repeated final copy of the first point is accepted, but is unnecessary. Do not repeat adjacent vertices or cross edges. The native limit is 10000 vertices per boundary ring; the circuit renderer and preview support at most 4096. Use the smaller limit for rendered tracks.
All boundary and hole coordinates must fit inside size. Every hole must lie
inside the outer polygon. Rings cannot intersect or touch, and holes cannot nest.
A hole is solid excluded space, not another room. Body and zone centres must lie in
playable space; place them with clearance from edges, because centre validation does
not guarantee that the entire hull or zone fits. Dynamic hull vertices are a
different kind of geometry: they are local coordinates around a body and must form
a convex polygon.
For example, add "holes": [[[12, 8], [16, 8], [16, 10], [12, 10]]] to a sufficiently
large scene to make a rectangular obstruction. First check that no starting body or
zone centre occupies it. An invisible collision obstacle is usually a hole whose
visual outline you have overlooked; turn on Collision geometry to inspect it.
Physics options#
|
Default and supported interval |
Effect |
|---|---|---|
|
|
Simulation time per frame. In JSON, write a decimal, not the expression |
|
Integer |
Divide each frame into this many integration/collision substeps. More substeps increase work. |
|
Integer |
Contact-impulse solver iterations per substep. |
|
Boolean |
When enabled, any controlled vehicle touching an outer wall or hole boundary ends the whole world; the wall penalty is still charged on that frame. Passive cargo and hooks do not trigger wall death. |
|
Boolean |
A body-body contact involving a controlled body can mark the whole world dead. |
A larger dt advances more simulated time per frame; it also changes the numerical
experiment. It does not simply speed up playback. Strong forces, light bodies,
stiff springs, and large steps can be a difficult numerical combination even when
each setting individually passes validation. Begin with the defaults, change one
quantity, and inspect a few manual frames before starting a large planning run.
Set Setup > World physics > Die on wall collision to change
physics.lethal_walls, then press Apply and restart. Every shipped preset
starts with wall death off and rewards.wall_collision: 100. An older imported
scene with explicit physics.lethal_walls: true keeps that setting.
Bodies and reusable agent types#
Every entry in bodies is a dynamic physical body. A passive asteroid still moves,
collides, and responds to gravity; controlled: false only removes actuator input.
For fixed obstacles use boundary geometry or holes. Indices used by tethers refer
to this complete array, starting at zero, including passive bodies.
Body field |
Default; supported interval |
Meaning |
|---|---|---|
|
Optional string |
Named entry in this scene’s |
|
|
Initial world position in metres. Supply an interior point explicitly. |
|
|
Initial world velocity in m/s; components must fit finite float32. |
|
|
Initial orientation in radians. |
|
|
Initial angular velocity in rad/s. |
|
|
Circle radius in metres when no |
|
Optional polygon |
Convex local hull with 3–32 vertices. Overrides circle geometry and derives the physical bounding radius. |
|
|
Kilograms. There is no zero-mass static-body mode. |
|
Computed; |
Rotational inertia in kg·m². Circle default is |
|
|
Linear velocity decay rate in s⁻¹. A substep of duration |
|
|
Angular velocity decay rate in s⁻¹. |
|
|
Force scale in newtons for vector, kart, and holonomic actuators. |
|
|
Torque scale in N·m for vector and holonomic actuators. |
|
|
Contact bounce coefficient. |
|
|
Contact friction coefficient. |
|
Boolean |
Compile action channels for this body. |
|
Boolean |
Marks a controlled body as eligible for automatic flight-mode detection. It does not add an action channel or provide hover assistance. |
|
Boolean |
Eligible for delivery and automatic cargo hooking. |
|
Boolean |
In |
|
Vector actuator if omitted |
Applied only to controlled bodies; options below. |
|
Optional object |
Appearance metadata; does not replace the physical hull. |
Hulls and initial conditions#
For a small rectangular body, use
"vertices": [[-0.8, -0.4], [0.8, -0.4], [0.8, 0.4], [-0.8, 0.4]].
These are offsets from the body origin before its angle rotates them. Centre a
custom hull sensibly around [0, 0]; the compiler does not recenter it for you.
Winding is normalized, but a concave hull is rejected. Increasing visual.scale
only enlarges the drawing. Change radius or vertices to enlarge collisions.
With keep_delivered_rocks: true, both mining and solo harvesting use the same
native delivery rule: the cargo centre must lie strictly inside the inner disk
at half the base radius. Every towing hook targeting that cargo detaches, but
the rock stays active and collidable, continuing to move under normal physics;
retention does not physically freeze it.
It is excluded from hooks and approach targets until its centre is strictly
outside every base’s outer radius, when it becomes eligible again. Crossing an inner disk
again during the lock does not count another delivery.
Both presets enable retention by default. Collaborative mining’s base is at
[12, 32], with outer radius 3 m and inner delivery radius 1.5 m. Turn
Keep delivered rocks off and press Apply and restart to restore
full-radius delivery and seeded random respawn for the presets’ cargo.
With keep_delivered_rocks: false (the compiler default), delivery uses the
full base radius. respawn: false then makes the cargo inactive; with true,
the same cargo body normally respawns immediately at a seeded random clear
position throughout the playable map, outside bases and with clearance from
walls, holes, and active bodies. It retains its configured angle, with zero
linear and angular velocity. If 256 placement attempts find no clear position,
the cargo stays delivered and inactive and retries on the next frame, without
counting another delivery.
Collaborative mining’s controller_defaults use horizon: 64, frames: 6,
and elites: 4; solo harvesting remains at 32, 6, and 4 respectively. The longer
lookahead helps plan coupled delivery, but does not guarantee success in every
stochastic run. See the mining guide.
Type definitions and inheritance#
Type field |
Meaning |
|---|---|
Object key |
Unique nonempty name used by |
|
Display label used by the agent picker; falls back to the type name. |
|
Optional parent type name in the same scene. Unknown parents and cycles are errors. |
|
Object of body-field defaults, including |
|
Object of appearance defaults. |
Resolution proceeds from parent type to child type to body instance. Overlays are
shallow: a child’s actuator replaces the parent’s entire actuator object. Arrays
such as vertices, thrusters, and visual parts replace the whole inherited array.
Visual object fields are overlaid separately by the browser. Type definitions do
not load other files; an exported scene must carry every type that its bodies use.
This fragment defines a drone type and a heavier child. Include it under the root
agent_types, and use the body fragment under bodies. The child’s mass changes
without changing its actuator. By contrast, adding "actuator": {"kind": "kart"}
to the child would replace the whole holonomic actuator.
{
"agent_types": {
"workshop_drone": {
"label": "Workshop drone",
"physics": {
"controlled": true,
"mass": 1,
"radius": 0.6,
"actuator": {"kind": "holonomic"}
},
"visual": {"model": "drone", "color": "#6ffff1"}
},
"heavy_drone": {
"extends": "workshop_drone",
"label": "Heavy workshop drone",
"physics": {"mass": 3}
}
},
"bodies": [{"agent_type": "heavy_drone", "position": [8, 8]}]
}
Actuators and action channels#
The actuator translates a bounded action into forces and torques. Changing its
kind can change both the meaning and number of controls. Actuator channels
shows the actual compiled channels for all controlled bodies in body-array order.
Use its sliders and Apply action · 1 frame to test a change before asking a
planner to use it. Channel values are dimensionless and clamped to their bounds.
|
Channels and bounds |
Physical interpretation |
|---|---|---|
|
|
Forward force along the body’s heading, plus independent rotational torque. No reverse thrust. |
|
|
Signed forward force, speed-dependent turning, lateral grip, and longitudinal braking. |
|
|
Two body-local force components, scaled by body |
|
|
One channel per thruster; |
Field |
Default; range |
Meaning |
|---|---|---|
|
Omitted means |
Per-channel scale for the compiled action range and the corresponding built-in force, torque, steering, braking, or thruster output. For example, |
Kart parameters#
|
Default; range |
Meaning |
|---|---|---|
|
|
Turning length scale. Target yaw rate is longitudinal speed divided by wheelbase, multiplied by the tangent of steering angle. |
|
|
Maximum signed steering angle at full input. |
|
|
Rate that removes lateral sliding velocity. Zero removes this grip force. |
|
|
Rate at which angular velocity approaches the steering target. |
|
|
Full-brake longitudinal deceleration; braking is limited to avoid reversing longitudinal velocity in one substep. |
A stationary kart does not turn merely because steering is nonzero: its target yaw
rate depends on longitudinal speed. Reverse speed also reverses that turning
relation. Body thrust sets its engine force, while body torque does not set kart
steering strength; use yaw_response. Braking acts along the kart’s forward axis;
lateral_grip handles sideways motion. This explains why increasing the brake
setting alone does not cure a sideways slide.
Independent thrusters#
Field in each |
Default; range |
Meaning |
|---|---|---|
|
|
Body-local mounting point in metres. Its offset creates a torque arm. |
|
|
Nonzero body-local direction, normalized by the compiler. Its magnitude does not increase force. |
|
|
Full-power force of this thruster; independent of body |
|
Boolean |
Allow negative channel input to reverse force. |
Supply between 1 and 32 thrusters. In this two-thruster fragment, equal inputs give
forward force with cancelling torque; unequal inputs turn the body. The live
keyboard does not map thruster_0 and thruster_1; use their sliders. Explicit
thruster forces and mounting points determine torque, independently of body torque.
{
"kind": "thrusters",
"thrusters": [
{"position": [-0.5, -0.4], "direction": [1, 0], "force": 6},
{"position": [-0.5, 0.4], "direction": [1, 0], "force": 6}
]
}
Zones, gravity, tethers, and reward#
The scene’s objects activate its mechanics. Putting food and gates in the same
scene enables both event systems, although the progress incentive gives priority
to the gates. Renaming task to "harvest" does not create cargo or delivery bases.
The important question is what arrays and body flags you have actually supplied.
Zones and event rules#
Field in |
Default; range |
Meaning |
|---|---|---|
|
|
Centre in metres; must be inside playable space. Supply an interior point. |
|
|
Event radius. Editor placement tools choose their own larger or smaller starting values. |
A base delivers active cargo whose centre lies strictly inside its full radius
when keep_delivered_rocks: false. With retention enabled, delivery uses half that
radius and excludes cargo still locked from a previous delivery.
A tug need not enter the base itself, and a tether is not a delivery prerequisite.
A gate counts when a controlled body’s centre lies strictly inside its next
zone. Each controlled body has its own ordered gate counter; the displayed gates
metric sums their gate events. The sequence wraps around indefinitely and checks
one gate per controlled body per frame. For task: "tandem", only bodies at the
group’s shared checkpoint stage can register a crossing; a body that has already
crossed waits for the others before its next checkpoint becomes eligible. The
stage follows the authored gates array order, as specified below. Neither the
base nor gate rule adds the body’s radius.
A pickup is collected when a controlled body approaches within the sum of its
bounding radius and the pickup radius. Only one vehicle gets that slot’s event in
a frame. Collection starts a cooldown of max(dt, respawn_seconds). At the end,
the slot moves to a seeded random playable position; after up to 64 unsuccessful
placement attempts it uses the configured initial position. The timer-expiration
frame does not also collect the newly respawned slot. These are simulation timers:
pausing pauses them. Cargo respawn has no cooldown; it attempts placement on
delivery and retries on subsequent frames if necessary.
Vehicle storage and unloading refineries#
Root cargo configures pickup storage for every controlled vehicle. It is a
different mechanism from a body marked cargo: true: a full harvester carries a
quantity internally, while an asteroid is a separate physical body. Refineries
unload vehicle storage; bases deliver separate cargo bodies. Neither zone
substitutes for the other.
Root |
Default; supported range |
Meaning |
|---|---|---|
|
Integer |
Number of pickups that fill each controlled vehicle. |
|
|
Time spent inside a refinery to discharge a complete load. |
|
|
Additional reward when a vehicle first fills its storage. An explicit value overrides the default. |
Add this fragment to a scene with controlled vehicles and pickups. Put the refinery
centre in playable space. Unloading refinery also places a refinery in the
editor, but the root cargo object is what enables vehicle storage.
{
"cargo": {"capacity": 5, "unload_seconds": 2, "full_reward": 10},
"refineries": [{"position": [8, 8], "radius": 3}]
}
Each collected pickup adds one storage unit. Reaching capacity awards full_reward
and switches that vehicle into its return phase. It cannot collect again until
completely empty. A partly filled vehicle still in its collecting phase cannot
unload: it must fill first. An empty cargo: {} enables the default five-unit
capacity and therefore still requires a refinery. Omit cargo to retain unlimited
pickup collection without a return cycle.
A returning vehicle unloads when its centre is inside or exactly on a refinery’s
radius. The amount per frame is capacity × dt / unload_seconds, capped by the
remaining load. It receives rewards.delivery × discharged_amount / capacity as
reward, so a complete load earns one delivery reward in total. Discharging the
last amount increments the global deliveries counter once and returns the vehicle
to collecting. Remaining inside multiple refineries does not multiply the rate.
Leaving the zone pauses discharge while retaining the remaining load and return
phase. Returning resumes it; pickup collection remains blocked throughout the
interruption. Discharge is processed before pickup collection each frame: a
newly filled vehicle cannot start discharging until a later frame, while a vehicle
that becomes empty may collect again in the same frame. A complete unload takes
approximately unload_seconds, rounded to frame resolution. Very small remaining
amounts are snapped to zero to finish the cycle.
Refinery capacity is shared without a queue or exclusive docking slot. Storage
changes neither body mass nor physical hull. Each controlled vehicle has four
additional state values: load, return phase, delivered units, and full-load cycles.
The selected-vehicle cargo status helps distinguish Collecting from Return /
unload. Built-in score and evaluation metrics still use pickups and deliveries;
there is no built-in metric named cargo or refined. When a scene combines
refineries with cargo-body delivery bases, deliveries counts both completed
vehicle unloads and delivered cargo bodies.
Gravity fields#
Field in |
Default; range |
Meaning |
|---|---|---|
|
|
Source location in metres. Unlike zone centres, this need not lie inside playable space. |
|
|
Positive attracts; negative repels; zero has no force. |
|
|
Smooths the force close to the source. |
For displacement d = source_position - body_position, gravity adds acceleration
strength × d / (|d|² + softening²)^(3/2). Every active body receives this acceleration,
regardless of mass. In flight mode, keep the picture of a constant downward pull:
a rocket or drone must continually produce enough upward propulsion to avoid
falling, while horizontal travel still lives in the same planar state. The glowing
source is a marker, not automatically a solid obstacle. Add a hole if you also
want an impassable central region. Larger softening spreads and weakens the
near-source field; it is not a collision radius.
Tethers#
Field in |
Default; range |
Meaning |
|---|---|---|
|
Integer |
Source body in the complete |
|
Integer |
Initial target; |
|
Initial endpoint distance if attached, otherwise |
Unstressed spring length. |
|
|
Spring restoring strength. |
|
|
Resistance to relative motion along the tether. |
|
|
Breaks when the required substep impulse magnitude exceeds this force times substep duration. |
|
|
Automatic attachment checks centre distance strictly below this range. |
|
Boolean |
Detached tether can acquire the closest active cargo. |
For an automatic tug, start with
{"a": 0, "b": -1, "automatic": true, "hook_range": 3}.
When it hooks cargo, the runtime rest length becomes the current centre distance,
with a minimum of 0.1 m. Thus rest_length is the initial spring setting, not a way
to force every later automatic attachment to a fixed length. Automatic tethers can
reacquire after breaking. Two tugs can attach to the same cargo; there is no
exclusive ownership rule. Keep sources distinct from cargo targets.
A tether is a spring joining centres, not a rope with editable hull attachment
points. Its restoring action can push as well as pull. A nonautomatic tether with
b: -1 remains detached. A fixed initial target may be any distinct body, not just
cargo. Reordering bodies requires updating every tether index; the editor’s delete
and duplicate operations do the corresponding remapping for you.
In mining scenes, Hook stiffness (N/m) adjusts tethers[].stiffness for
the mining hooks. Pause the simulation, change the slider or numeric input, then
click Apply rock settings to restart with the new setting. The same controls
allow Rock size from 0.1× to 2× and Rock weight from 0.01× to 10×. Low stiffness lets
the hook stretch like a rubber band; high stiffness keeps its length approximately
fixed. This remains a spring, so high stiffness is not an exact rope constraint.
Setting stiffness to zero removes the restoring force but leaves radial damping
active. The solver applies an implicit spring-and-damper impulse along the line
between the bodies. It does not constrain tangential motion, so stiffness alone
does not prevent a rocket from moving around its cargo.
Reward settings and priority#
|
Default for other tasks |
Default for |
Range |
Contribution |
|---|---|---|---|---|
|
|
|
|
In tandem with gates, weights the checkpoint-proximity score once per physics frame, using all controlled bodies. Other tasks and tandem scenes without gates use the change in target-distance potential. |
|
|
|
|
Multiplies the mean squared displacement of controlled vehicles in each physics frame, measured in m². |
|
|
|
|
Reward per metre of travel summed over distinct active cargo bodies hooked to active controlled vehicles at the start of each physics frame. |
|
|
|
|
Penalty for qualifying vehicle/body contacts only; excludes walls and hole boundaries. Harvest disables this body-contact penalty. |
|
|
|
|
Penalty once per controlled vehicle per physics frame touching an outer wall or hole boundary, in every Control Lab task, including harvest and mining. |
|
|
|
|
Reward per collected food slot. |
|
|
|
|
Reward per cargo-body delivery, or total reward distributed across unloading one full vehicle load. |
|
|
|
|
Reward per controlled-body gate event in other tasks; in tandem, each eligible crossing receives this weight divided by the total controlled-body count. |
|
|
|
|
Multiplies the pairwise formation score once per physics frame for |
These defaults apply when a field is omitted. Formation flight rewards proximity
to and crossing of synchronized checkpoints, squared vehicle travel, and formation;
the two collision penalties also remain enabled. All other reward terms default
to zero. Explicit custom weights are honoured, including zero. The separate
cargo.full_reward defaults to zero for tandem even if you enable
rewards.pickup; set it explicitly to award a full-load bonus. Setting both
gate and progress to zero removes those rewards while preserving checkpoint
synchronization and its counters.
In other tasks, Target progress weights the change in a potential equal to
negative target distance, averaged over controlled bodies. Tandem scenes without
gates retain this target-distance fallback. Target selection follows this priority:
the next gate, if any gates exist; otherwise the nearest refinery while a
vehicle is in its cargo return
phase; otherwise the nearest active pickup, if pickup slots exist; otherwise,
when bases exist, either the nearest active cargo or the attached cargo’s distance
to the nearest base. Refinery distance is measured to the zone edge and is zero inside it, while
other target distances use centres. A returning vehicle keeps its refinery target
even after partially unloading. Gates still take priority over that return target.
A temporarily empty pickup field does not switch to cargo-body navigation. With no relevant target the distance contribution is zero.
For tandem, the same rewards.progress field is labelled Checkpoint proximity.
When gates are present, it weights the following score at its unchanged default
of 1.
Definition 3 (Synchronized tandem checkpoint rewards)
For task: "tandem" with \(N>0\) controlled bodies and \(G>0\) gates, let \(c_i\) be
body \(i\)’s existing gate counter at the start of a physics frame. Define the
shared stage \(s=\min_i c_i\) and eligible set \(E=\{i:c_i=s\}\). During that frame,
only bodies in \(E\) can register crossings of gate gates[s % G]. Bodies with
higher counters wait for their next crossing, but still contribute to proximity.
The stage and eligible set stay fixed throughout the frame; a newly unlocked
stage takes effect next frame.
Let \(R>0\) be that gate’s radius in metres, and let \(\ell_i\) be the distance between body \(i\)’s centre and the gate’s centre after physical motion and before scene mechanics and respawns. Average these distances across all \(N\) controlled bodies, including those that have already crossed, then transform that mean:
where \(K_t\) is the number of eligible bodies whose centres lie strictly inside
the current gate during event processing. Each such crossing increments that
body’s counter and the existing total gate counter once. A waiting body cannot
receive another crossing bonus at that stage. Proximity is one group reward per
physics frame, with no timestep multiplier; its score is dimensionless and
positive for finite distances. The weights are rewards.progress and
rewards.gate; zero weights do not change the eligibility or counter rules.
With two rockets and the default checkpoint bonus of 30, the first arrival earns 15 and waits for its next crossing opportunity. Both rockets’ distances still affect proximity while the second approaches. The second arrival earns the remaining 15, and the next checkpoint unlocks on the following physics frame. The arrival frame uses the old checkpoint for proximity. Formation and movement rewards remain active while a rocket waits. No extra scene field or counter is needed: the minimum of their existing counters determines the stage. Other tasks retain their independent checkpoint progression and full per-body gate bonuses.
For a checkpoint of radius 2 m and two rockets at centre distances 0 m and 6 m, the mean distance is 3 m, so proximity pays \(2/(2+3)=0.4\) at weight 1. Average the distances first; averaging the two separately transformed scores would give a different reward. For a fixed shared checkpoint, keeping the same positions earns the same positive proximity reward on each physics frame. Moving away lowers this score rather than producing a negative distance-change reward. Setting Checkpoint proximity to zero disables this bonus independently of formation and checkpoint crossing rewards.
For cargo hauling, the target used to measure progress is held fixed through each physical frame. If a hook breaks during that frame, both distance measurements still use the same hauling target; the new detached target takes effect next frame. Breaking a hook therefore cannot earn a progress bonus merely by switching from cargo-to-base distance to rocket-to-cargo distance.
Reward values use the scene’s chosen reward scale: event coefficients are reward per event, target-progress weights convert a distance change into reward in other tasks and tandem scenes without gates, and tandem Checkpoint proximity with gates weights a dimensionless score per physics frame. Body-contact penalties can accumulate across substeps and repeated contacts. The separate wall penalty is charged once per controlled vehicle per physics frame: sustained contact costs again every frame, but corners, multiple boundary contacts, and substeps do not add extra charges within a frame. Passive cargo and hooks incur no wall penalty and cannot trigger wall death. A lethal wall contact still incurs the penalty on the death frame.
Harvest allows progress, distance_squared, catch, and wall_collision
reward terms; disabling its body-contact penalty does not disable wall penalties.
Proximity and target-progress measurements precede event bookkeeping updates to
the next target. Gate, food, and delivery bonuses are then added independently.
A large task counter and a low total reward are therefore compatible.
The distance_squared term pays for motion in any direction. In each
physics frame, measure each controlled vehicle’s displacement \((\Delta x_i,
\Delta y_i)\) and add
where \(N\) is the number of controlled vehicles and \(w_{\mathrm{distance}}\) is
rewards.distance_squared. Stationary vehicles contribute zero, and cargo bodies
are excluded. With no controlled vehicles, the contribution is zero.
The runtime measures this motion before scene mechanics and respawns, so a respawn
does not earn a travel bonus. It sums these frame rewards; it does not square the
total path length. For example, two frames with a 1 m displacement each contribute
\(2w_{\mathrm{distance}}\), whereas one frame with a 2 m displacement contributes
\(4w_{\mathrm{distance}}\). Changing the physics timestep therefore changes this
reward’s scale, even for the same path and speed. The coefficient converts m² per
frame into reward per frame. Its default is one, so motion is valuable even away
from a target. Set it explicitly to zero to disable this bonus. Older scenes that
omit distance_squared also receive the default weight of one.
The Hooked rock travel weight, rewards.hooked_rock_distance, pays for the
rock’s movement. At the start of each physics frame, collect the distinct active
cargo bodies attached by a hook to an active controlled vehicle. For each of
these rocks, measure its centre displacement during physics integration and add
the travelled distance times the weight. Two rockets attached to the same rock
earn this contribution once, while two attached rocks contribute their summed
distances. A rocket circling a stationary rock earns zero from this term; moving
the rock in any direction earns reward, including moving it in circles. Progress
and delivery rewards supply the incentive to move it toward a base.
This distance is Euclidean, in metres: it is neither squared nor multiplied by
the timestep. Measurements happen before scene mechanics and respawns, so a
teleport on delivery earns no travel bonus. Hook attachment or release changes
which rocks qualify on the next frame. The coefficient defaults to zero for
tandem and one reward unit per metre for other tasks, including for scenes that
omit it. Set Hooked rock travel to
zero and press Apply to current run to disable it independently of vehicle motion
and target progress rewards.
The Lab’s Rewards panel is expanded by default. Use its synchronized
sliders and numeric inputs to set these weights and cargo.full_reward, the bonus
for first filling vehicle storage. The same panel exposes Diversity coefficient
and Reward coefficient, which control FMC selection.
Use Rewards > Wall collision penalty to set rewards.wall_collision from
0 to 10000. Press Apply to current run to apply it live while preserving the
current physical state. A value of zero disables the wall penalty independently
of Die on wall collision.
Older imported scenes that omit wall_collision receive the default of 100. This
changes their future and resimulated rewards; historical stored records are not
rewritten. The snapshot layout is unchanged.
Edits remain pending until you press Apply to current run. This shared button applies
both the coefficients and reward weights, keeps the current physical state,
discards previous plans, replans, and starts a new recording. If the simulation was
running, it resumes running. Recordings and exports retain the settings actually
applied rather than pending edits. Reset defaults stages the engine defaults
for the current task, using the tandem column above for formation flight; press
Apply to current run to use them. Formation reward changes its independent
weight, including when progress is zero.
Pairwise formation distances#
Choose the distance you want between each pair of rockets, then compare it with their actual centre separation. A pair at its requested distance contributes one. An error on either side lowers its contribution. Multiply the contributions from all pairs to measure the whole formation. This uses distances alone, so moving or rotating the entire group leaves its score unchanged.
Definition 4 (Control Lab formation reward)
For task: "tandem", let \(\mathcal C\) be the set of controlled-body indices and
let \(x_i\in\mathbb R^2\) be body \(i\)’s centre after the frame’s physical motion and
before scene mechanics and respawns. For each unordered pair \(i<j\) in
\(\mathcal C\), let \(d_{ij}^{*}>0\) be its configured target separation in metres.
The dimensionless formation score and its reward contribution are
where \(w_{\mathrm{formation}}\) is rewards.formation. Other tasks receive no
formation contribution. Each pair appears once, including pairs without an
explicit override. The engine adds this contribution once per physics frame,
independently of checkpoint proximity and crossing bonuses; it is not multiplied
by the physics timestep or divided by the number of pairs.
For a 5 m target and a 6 m separation, the pair contributes \(5/(5+|5-6|)=5/6\). A 4 m separation earns the same value. With three rockets, multiply the scores for A–B, A–C, and B–C. Perfect agreement gives one; increasing any separation error lowers the product toward zero. With positive finite target distances and finitely many pairs, the mathematical product is positive for finite positions, though floating-point arithmetic can round a very small product to zero. Arbitrary hand-picked distances need not be geometrically compatible, so some configurations cannot attain a score of one.
A stationary group still earns formation reward every frame. Keeping its
separations constant while flying also preserves this reward, while
distance_squared separately rewards motion. Setting rewards.progress to zero
does not disable formation; setting rewards.formation to zero does. A step that
advances several physics frames sums their formation contributions.
Enable Tethers & formation to see a dashed line between the displayed centres
of every unordered controlled-body pair: two vehicles produce one line, three
produce three, and four produce six. The endpoints follow the displayed vehicles
during live motion, while paused, and in replay. Formation lines appear only for
task: "tandem" with at least two controlled bodies and the overlay enabled.
Each line’s color shows its pair’s unweighted distance-agreement factor above,
using formation_pairs and the formation_distance fallback. The fixed,
continuous scale runs from rose/red at 0 through amber at 0.5 to green at 1 in
both visual styles. Read it against Pair quality: 0 — 0.5 — 1 · Perfect beneath
Tethers & formation. Multiplying the line scores gives the total formation
score. These lines remain visible when rewards.formation is zero: they show
geometric agreement, independently of how much reward you assign it.
Use Edit complete scene JSON to set pair distances. This fragment requests
5 m between bodies 0 and 1 and 6 m between bodies 1 and 2. If all three bodies
are controlled, their remaining pair, 0 and 2, uses formation_distance: 4.
{
"task": "tandem",
"formation_distance": 4,
"formation_pairs": [
{"a": 0, "b": 1, "distance": 5},
{"a": 1, "b": 2, "distance": 6}
]
}
Each entry must be an object with all three fields. a and b must be numeric
integers naming distinct controlled bodies in the complete zero-based bodies
array, which also includes passive bodies. distance must be a finite number
in [0.1, 1000] metres. Strings, booleans, missing fields, invalid indices,
passive endpoints, self-pairs, and duplicate unordered pairs are rejected;
{"a": 1, "b": 0, "distance": 5} duplicates an entry for bodies 0 and 1.
An omitted formation_pairs array leaves every pair on the scalar fallback.
Omitting formation_distance uses 3 m; the shipped formation preset retains its
4 m target. Overrides select target distances, not which pairs are scored.
The compiler prepares these targets once for use during stepping.
When deleting bodies in the editor, it removes overrides touching those bodies
and remaps the surviving indices. Duplicating a selected group copies overrides
whose two endpoints are both in that group to the new bodies. Other new pairs
use the scalar fallback. If you reorder the JSON bodies array by hand, update
the pair indices yourself, just as for tethers.
The scene version and snapshot layout are unchanged. Previously stored reward history remains as recorded; future simulation and resimulation use this pairwise formula and the current reward settings. See the formation-flight guide for a worked experiment.
Appearance and circuit metadata#
Physical fields determine the experiment. Appearance fields help you read it.
Change a model’s color freely, but use Collision geometry after changing its
size or shape: visible wings, wheels, and kit parts do not become new collision
surfaces. The Visual style picker is a separate application setting; adding an
arbitrary style key to scene JSON does not configure that picker.
Body visuals and declarative model kits#
|
Values and defaults |
Effect |
|---|---|---|
|
|
Selects a registered model. Without a visual object, controlled forage bodies use kart fallback, other controlled bodies rocket fallback. With a visual object but no model, the model factory defaults to rocket. |
|
Color value; use a CSS hex string such as |
Sets the identification ring under authored asset vehicles, whose bodywork retains its palette. Procedural model factories use it as their supplied color; kit parts inherit it unless they specify |
|
|
Positive visual scale multiplier. Body display also scales by configured radius (fallback 0.5) divided by 0.8. |
|
Required for |
Declarative visual components, described below. |
The controlled-body layer uses these vehicle models. Passive cargo is drawn as ore;
it does not become a driven kart merely by adding visual.model. The renderer’s
radius-based scaling uses the resolved scene field; a native convex hull may derive
a different bounding radius. Supply a sensible radius for appearance as well when
using custom vertices, and inspect the overlay.
Kit part field |
Values/default |
Meaning |
|---|---|---|
|
Required: |
Primitive geometry. |
|
|
Box: three dimensions; sphere: radius; cylinder/cone: radius and height; ring: major and tube radius. One to three entries, with at least the number needed by that shape. |
|
|
Three finite local visual coordinates. |
|
|
Three finite local Euler angles in radians. |
|
Parent color |
Per-part color override. |
|
False if omitted |
Use a glowing material. |
|
Optional |
Visual animation driven by displayed simulation time, speed, and input. |
A minimal visual kit is
{"model": "kit", "parts": [{"shape": "box", "size": [1.2, 0.6, 0.3]}]}.
Thrust parts appear with positive thrust and stretch; steering parts rotate around
local z, wheels around local y, and rotors around local z. These animation tags do
not add actuator channels or torque. The visual model has three coordinates because
it is a 3D drawing of a planar body. In flight mode, the default side-on rendering
lets you read x as horizontal travel and y as altitude; the overhead 2D view
is useful for inspecting the route without changing the underlying planar physics.
Environment renderer#
|
Values/default |
Meaning |
|---|---|---|
|
|
Registered environment renderer. Arena needs no additional metadata. |
|
Optional Boolean; auto-detected when omitted |
Selects the side-on flight presentation and constant downward gravity; explicit |
|
|
Downward acceleration used by flight physics. It is ignored when flight mode is disabled. |
|
Required for circuit: 3–4096 finite |
Closed decorative route guide; not a collision boundary or checkpoint generator. |
|
Required for circuit: |
Decorative circuit/start-line width. Actual road bounds remain |
|
Optional start object; position required within it |
Finite |
|
|
Orientation of start decoration. Supply explicitly for the preview arrow too. |
|
Optional sponsor object; position required within it |
Finite |
|
|
Side length of its decorative square. |
The circuit renderer requires an explicit valid boundary even though native
physics can supply a default rectangle. Its centerline is drawn as a closed loop.
Start-line metadata does not move the agent or award laps. Set body starting poses
and actual gates separately. When reshaping a track, update all three: physical
bounds, checkpoint positions, and decorative centerline.
|
Meaning |
|---|---|
|
Associates the scene with a known Select track catalog entry. It does not load or replace geometry by itself. |
|
Preview name; otherwise scene name or |
|
|
|
|
|
Array of source metadata. The preview uses the first entry’s |
Score, experiment goals, and error recovery#
There are three different questions here. The score says what the display is counting. Reward tells the planner how its simulated actions performed. The evaluation goal tells a batch experiment when to call a trial successful. None of these should be inferred from the scene’s title.
Presentation fields#
Field |
Meaning and validation |
|---|---|
|
Text above the viewport describing the task. |
|
Built-ins: |
|
Score label text. Supply it with a custom score object. |
|
Finite positive number, default |
|
Same built-in metrics, optionally displayed as a cycling next-target label. |
|
Text such as |
|
Required positive integer when progress is present. Display is |
Default task presentation is forage → food collected, tandem → gates crossed,
navigation → gates crossed, and harvest → cargo deliveries. Other task strings
use the navigation presentation fallback. Overrides are shallow: if you replace
score, supply its metric and label together. For a one-agent 16-gate circuit,
use score.divisor: 16 and progress.cycle: 16; this displays completed laps and
the next checkpoint. With multiple agents, the global gates metric is a sum, so
that same display does not represent each driver’s individual lap.
The reward presentation metric reads the native most-recent step result; it is
not the accumulated experiment reward. Dividing and flooring also hides fractional
values. For accumulated results, inspect the experiment report described in
Experiments, comparisons, and performance.
Experiment success and termination#
Field |
Meaning |
|---|---|
|
|
|
Required finite number strictly greater than zero. Event targets may be fractional, but integer counts reach them only at the next whole event. |
A batch experiment’s explicitly supplied goal overrides scene evaluation. Without
either, the goal is survival for the episode’s maximum frame count. Survival counts
frames advanced during that episode, not seconds. Reward evaluation accumulates
reward during the episode. Event evaluations read world counters, including any
counts already present when starting from a recorded root.
The experiment advances one frame at a time within each chosen action, checking success, death, and the frame limit. A dead world does not count as successful even if it also reaches the target. In ordinary live operation, collecting the target number of objects or finishing a lap does not itself terminate the world. Lethal collisions can do so. For runtime controls, recording, and comparing trials, see Controls, planners, and diagnostics, Record, replay, and continue experiments, and Experiments, comparisons, and performance.
Extensions and capacity limits#
JSON cannot install executable behavior. Custom actuator.kind, visual.model,
environment.kind, and extension kind values must already be registered in the
respective native or browser application. The native world-extension registry has
no default built-in kinds. Adding {"extensions": [{"kind": "wind"}]} to an
ordinary build therefore fails; it does not create wind. See
Engine architecture and extension guide for the registration interfaces.
The native scene file is limited to 8 MiB and JSON nesting to 64 levels. Native parsing rejects duplicate object keys and non-finite numbers; avoid duplicate keys even in the browser, where ordinary JSON parsing keeps the last occurrence. The combined action space is limited to 8192 channels. A registered actuator can expose 1–64 channels and up to 256 initial state values; plugin channels need nonempty names and finite strictly increasing bounds. Built-in action multipliers may reduce a channel to equal zero bounds when a degree of freedom is disabled. These limits do not enlarge the built-in 32-thruster limit. There may be at most 64 world extensions, each with at most 4096 initial state values and 4096 observation values. Combined auxiliary state and extension observations are each limited to 65536 values, and total world state to 100000 float32 words. Enabled vehicle storage adds four state words per controlled body and counts toward that total. A scene below the body limit may still exceed one of these combined limits.
Diagnose a failed edit#
Save a known-good export before large JSON changes. Parsing, native compilation, renderer construction, and experiment-goal validation happen in different places. The complete JSON dialog catches syntax errors and its missing-body check locally. A valid JSON edit can close the dialog and subsequently report an asynchronous worker or renderer error. The dialog closing is not proof that the experiment is ready. Inspect the application status and wait for the rebuilt world before driving.
Native validation uses the fields it knows; it is not a strict schema rejecting every unknown key. A misspelled optional field may have no effect rather than produce an error. Keep exact names from these tables, inspect the exported scene, and test the resulting behavior. Passing compilation checks parameter ranges and geometry; it does not guarantee a useful objective or numerical stability.
Symptom or error |
What to correct |
|---|---|
JSON syntax error |
Remove comments and trailing commas; quote keys and strings; use decimal numbers rather than formulas. |
|
Restore a nonempty |
|
Replace strings such as |
|
Compare every changed field with its table; verify units. |
|
Move the centre away from outer edges and holes; enlarging |
Boundary, ring intersection, or nested-hole error |
Remove crossing/touching edges, repeated adjacent vertices, or nested holes. |
|
Replace the concave local hull with a convex shape; use holes for concave fixed scenery. |
|
Add an interior |
Vehicle cannot collect or unload |
A full or partially discharged vehicle must finish unloading; a partly filled collecting vehicle must fill first. Inspect the cargo phase and refinery centre-distance condition. |
Unknown or cyclic agent type |
Embed the referenced definition and repair |
Unknown actuator/model/environment/extension |
Use a built-in name or load a build that registers that feature. |
Visual scale, kit dimensions, or circuit geometry error |
Correct renderer fields as well as native fields; include explicit circuit bounds and centerline. |
Invalid score divisor/progress cycle |
Use a finite positive divisor and positive integer cycle. |
Invalid episode success criterion |
Set a supported evaluation metric and finite positive target. |
Numerical-limit error after compilation |
Return to a stable revision; reduce excessive force or stiffness, increase mass where appropriate, or use a smaller step and test manually. |
Score advances while nothing moves |
Check overlapping/one-gate routes; cargo initially inside a base also counts as a delivery. |
After an unsuccessful applied edit, try Undo to recompile the preceding scene revision. If the application cannot recover, reload and import your saved valid export, or reselect a shipped environment. Scene Undo restores definitions; it does not restore the run you had before editing. Export recordings separately before changing the experiment itself.