---
title: "Named values: the numbers a machine runs on"
description: "A machine made of parts needs values every part can see: throttle, rpm, gear, wheel speed, temperature, damage, lights on, a door open."
section: "Topics"
slug: "topics-values"
canonical: "https://origozero.ai/docs/topics-values"
updated: "2026-10-26T11:50:21.882566571+00:00"
---

# Named values: the numbers a machine runs on

```lua
local car = entity.find("car")
car.values.set("rpm", 3200)
car.values.set("brake", true)
car.values.set("spin", vector.create(0, 12.5, 0))
print(car.values.get("rpm"))          --> 3200
```

The `values` global takes the same calls with the entity first, which is the
form for code that holds ids rather than proxies:

```lua
values.set(car, "rpm", 3200)          -- or values.set(car.id, ...)
values.get(car, "rpm")
values.list(car)                      --> { rpm = 3200, brake = true, spin = ... }
values.remove(car, "rpm")             -- or values.set(car, "rpm", nil)
```

A name takes the kind of its first value and keeps it: writing a boolean to a
name that holds a number raises, and `remove` frees the name for another kind.

## What a value is, next to a component field

A **component field** is authored state: saved with the scene, edited in the
inspector, and, when it is `Sync`, replicated on every write. A **named value**
is live runtime state: never saved, never shown as a field, and local to this
engine unless it is declared to replicate. A gauge reading, an engine's rpm, a
wheel's slip or a light's on/off switch written every frame belongs in a value;
keeping it on a `Sync` field sends every write to every peer.

A value lives as long as its entity: it goes when the entity leaves the world.
Each mode starts with none: entering play or going back to edit empties every
value, and a subscription stands across the change and is told its value went.

A write is a store: about the cost of a table write plus a call, with nothing
queued and nothing sent. Every reader sees it on its next read, whichever of
them it is.

## Declaring a value, and replicating it

`declare` states what a name is before anything writes it: its `type`, the
`default` it starts at, and whether it replicates.

```lua
car.values.declare("throttle", { type = "number", default = 0, replicate = 10 })
car.values.declare("headlights", { default = false, replicate = 2 })
car.values.declare("gear", { type = "number" })      -- kept on this engine
```

`replicate` is a rate in Hz (up to 60). The peer that may write the entity
(its owner, or any peer while no peer owns it) sends the value to every other
peer at most that often while it changes, and always sends the newest value,
so a throttle written every frame at 60 fps and declared at 10 Hz reaches the
others ten times a second and ends on the value it settled at. The entity has
to be synced (`entity.spawnSynced`, `setSynced(true)`): an entity no other peer
shares has no one to receive it. A value declared before that, or in the frame
the entity spawns, is sent as it stands once the entity is synced, and so is
every replicated value of an entity this peer takes over, so a `headlights`
declared once and never written again still reaches every peer. On the other
peers the value is an ordinary value, written by `remote`; a peer that does
not own the entity cannot send it.

A value with no `replicate` rate is never put on the wire, however often it
changes. `values.stats().replication` counts what was sent and received.

## Following a value: subscriptions

```lua
local conn = car.values.subscribe("gear", function(gear)
    print("now in gear", gear)
end)
values.subscribe(car, nil, function(value, name) end)   -- every value of car
values.onAnyChange(function(value, name, entityId) end) -- every value anywhere
conn:Disconnect()
```

A callback runs once per frame in which the value changed, whoever changed it
(a script, an engine system or another peer), with the value as it stands at
that point; a value that went arrives as `nil`. A subscription a component
makes ends with the component. Each VM hears the changes its own
subscriptions follow: a job on a worker following a value hears it, and the
engine thread's subscriptions to the same value hear it too. `onAnyChange` with `values.list` is what a
recorder of a session's values follows: the list for where every value starts,
each change after it as it happens.

## A material that reads a value

A material binds any of its shader's scalar, vector, colour or bool
properties to a value in its `values:` block, and the property then draws the
value of the entity wearing the material:

```yaml
# brake_lamp.material/mat.yaml
shader: "@builtin::shaders.pbr"
colors:
  base_color: { r: 0.3, g: 0.02, b: 0.02, a: 1 }
  emissive: { r: 1, g: 0, b: 0, a: 1 }
floats:
  emissive_intensity: 0
values:
  emissive_intensity: { value: "brake", scale: 3 }
```

The lamp glows at intensity 3 while its entity's `brake` value is true and at
0 while it is false: `scale` and `offset` map the value as `value * scale +
offset`, a boolean reads as 1 or 0, and a number fills every channel of a
colour or vector property. An entity holds the value itself, or **an ancestor
holds it**: a lamp mesh under the car reads the car's `brake`, so one value on
the root drives every part of the machine. An entity that holds no such value
(nor any ancestor of it) draws the material's own value. The read happens on
the draw, so a thousand lamps wearing one material each light from their own
car with no script and no material per car.

A runtime material binds the same way: `renderer.material.create({ shader =
..., values = { emissive_intensity = { value = "brake", scale = 3 } } }, key)`,
or `asset.create("material", ..., { values = ... })`. A binding names a
property the shader declares; a texture slot takes no value. A material binds
at most 32 properties.

## An audio voice that follows a value

`Audio` takes `pitchValue` and `volumeValue`, each the name of a value: the
voice plays at `pitch` times that value and `volume` times that value, read
from the source's entity or its nearest ancestor every frame.

```lua
car.component.add("Audio", { clip = engineLoop, looping = true, pitch = 0.5, pitchValue = "rpmRatio" })
car.values.set("rpmRatio", rpm / 1000)   -- the engine note rises with it
```

## A UI widget bound to a value

`values.bind(entity, name, opts)` stands in a widget's `text`, `title`,
`header`, `placeholder`, `value`, `min`, `max`, `visible`, `enabled` or
`checked`, and the widget reads the value every frame with no `updateScreen`:

```lua
ui.registerScreen("dash", { type = "vertical", children = {
    { type = "label", props = { text = values.bind(car, "rpm", { format = "%d rpm" }) } },
    { type = "slider", props = { min = 0, max = 1, value = values.bind(car, "throttle") } },
    { type = "label", props = { text = "BRAKE", visible = values.bind(car, "brake") } },
} })
```

`format` is a `string.format` pattern with one conversion (`%d`, `%.1f`, `%s`,
`%x`, `%g`); without one a number reads as it is. `%d` and `%x` take the whole
part of any number, so `"%d rpm"` shows 3215 for 3215.7. `scale` and `offset` map a
number first. `fallback` is what the widget shows while the entity holds no
such value (empty text, 0 or false when absent).

## Values the engine writes

Engine systems publish their own state as values, under a prefix naming the
system, on the entity the state belongs to. A `WheelCollider` writes, every
physics tick, on its wheel entity:

| Name | Kind | What it is |
|---|---|---|
| `wheel.compression` | number | suspension compression |
| `wheel.angularVelocity` | number | spin, radians per second |
| `wheel.skid` | number | how hard the tyre slides |
| `wheel.slipRatio` | number | longitudinal slip |
| `wheel.slipAngle` | number | slip angle, radians |
| `wheel.roadSpeed` | number | contact speed along the road, m/s |
| `wheel.load` | number | suspension load, newtons |
| `wheel.forwardForce` | number | drive or brake force, newtons |
| `wheel.sideForce` | number | cornering force, newtons |
| `wheel.grounded` | boolean | whether the wheel touches ground |

So a tyre-squeal voice on the wheel takes `volumeValue = "wheel.skid"`, and a
gauge binds `wheel.roadSpeed`, with no script reading the wheel.

## Seeing every value

`values.describe(entity)` lists an entity's values with their type, last
writer (`script`, `engine`, `remote`), revision and replication rate, and
`values.entities()` names every entity holding one. The `values` toolbox is
the same view as tools (`zero values list`, `zero values describe car`,
`zero values set car brake true`), and the files `/zero/runtime/values` and
`/zero/runtime/values/<entityId>` hold it as JSON, read again on each read.
