Properties
A Properties object is a persistent, string-keyed store attached to a game object — see Properties for what the system is and how it behaves in multiplayer.
Reached via world.getPropertyObject(objectId) (see World), a car’s properties (see Car), or scoped() below.
Reading and Writing
Section titled “Reading and Writing”Values are read and written with [key], Relay’s index syntax — there’s no get/set method pair. A read hands back a plain value, not a wrapper with typed accessors. Any JSON-like value works: null, bool, int, float, string, arrays, and dictionaries.
var props = world.getPropertyObject("some-car-id")props["mymod.visits"] = (props["mymod.visits"] ?? 0) + 1A key that has never been set reads as null. null is falsy, so a bare read is fine in a boolean test — if (!properties["owned"]) — and compares safely with ==/!=. It is not safe in arithmetic or ordering comparisons: properties["oiled"] < 0.99 and properties["visits"] + 1 both throw on an unset key. Supply a default with ?? before doing either, and mind the precedence — ?? binds looser than +, so props["visits"] ?? 0 + 1 parses as props["visits"] ?? (0 + 1). Parenthesize:
var next = (props["mymod.visits"] ?? 0) + 1Use as Integer where the stored type isn’t under your control, and defaultProperties on a car definition to make keys start out set so reads are never null in the first place.
var props = world.getPropertyObject("some-car-id")props["mymod.counter"] = (props["mymod.counter"] ?? 0) + 1Observing
Section titled “Observing”observe(key, callInitial, callback)
→ Disposable key string callInitial bool If true, callback also fires immediately with the current value callback (any) => void Called with the new value whenever it changes Subscribes to changes on key. Returns a disposable — call dispose() to unsubscribe.
var sub = props.observe("mymod.tag", true, (value) => { print("mymod.tag is now \(value)")})
// Later:sub.dispose()See Observing changes for when to dispose, and UIPanelBuilder.addObserver() for tying a subscription to a UI panel’s lifetime.
Scoped Views
Section titled “Scoped Views”key string Returns a nested Properties view backed by a dictionary stored under key on this object. Reads, writes, and observe() on the returned object all address that sub-dictionary, letting a mod namespace a group of related keys under one entry instead of flattening them into the parent store.
var props = world.getPropertyObject("some-car-id")var mymod = props.scoped("mymod")mymod["tag"] = truemymod["count"] = 3scoped() is only valid on a top-level object returned by getPropertyObject()/car.properties — calling it on an already-scoped view throws.
© 2026 Giraffe Lab LLC. All rights reserved.