Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Tutorial:Data Model Flow

From Resonite Wiki

This guide covers Resonite's synchronized data model in depth — from the basic World/Slot/Component/Field hierarchy, through value vs reference types, data containers, drivers, ProtoFlux bridges, and wiring patterns. It is intended as a comprehensive reference for builders and developers who need to understand how data flows through Resonite.

An interactive version of this guide with rendered diagrams is available at resonite-data-model-guide.netlify.app.

The Big Picture

Everything in Resonite is a synchronized data model. There is no "game logic" separate from data — the world is the data, and changes propagate automatically across all users.

World → Slot → Component → Field hierarchy
World → Slot → Component → Field hierarchy
Key insight: A Resonite world is a tree of Slots. Each Slot holds a list of Components. Each Component has typed Members (fields). That's the entire architecture.
Concept Analogy What it holds
World Scene file One root slot tree + users + settings
Slot GameObject / Transform Position, rotation, scale, active flag, child slots, components
Component MonoBehaviour Typed members (fields) specific to its purpose
Member / Field Serialized field A single typed value: float, string, reference, etc.

Workers & Sync Elements

Under the hood, Resonite's data model is built on Workers — objects that participate in the synchronization system.

Worker base class — Slot and Component
Worker base class — Slot and Component

Worker

Base class for anything that lives in the data model. Has a unique persistent ID. Slots, Components, and Users are all Workers.

Sync Element

The atomic unit of synchronization. Every field on a component (like Position on a Slot, or Size on a BoxMesh) is a Sync Element. When it changes, the change is replicated to all users.

Users are Workers too. Every user in a session is a full Worker with their own slot hierarchy under the world root, marked by a UserRoot component. That hierarchy contains the user's avatar, head, hands, controllers, locomotion modules, audio streams, and a User dynamic variable space. User components work like any other — they have Sync Elements, can be driven, and are inspectable. Per-user state (like IK, dynamic bones, or user-specific UI) is typically achieved through driven fields, which are computed locally on each client rather than synced over the network.

Change Propagation

When you modify a Sync Element:

  1. The local value updates immediately
  2. A change event fires locally (drivers, ProtoFlux nodes react)
  3. The change is queued for network replication
  4. Remote clients receive and apply the change
  5. Remote change events fire on those clients

Value Types vs Reference Types

This is the most important distinction in Resonite's type system. It determines which components and ProtoFlux nodes you use.

Value types vs Reference types — string and Uri are value types in FrooxEngine but object types in ProtoFlux
Value types vs Reference types — string and Uri are value types in FrooxEngine but object types in ProtoFlux
The dual-nature gotcha: string and Uri are value types in FrooxEngine's data model — they use ValueField<T>, DynamicValueVariable<T>, etc. However, in ProtoFlux they are object types — use ObjectWrite<string> and ObjectValueSource<string>, not ValueWrite<string>. See Value_type.
Value Types (structs) Reference Types (classes)
bool, int, long, float, double
float2, float3, float4, floatQ
colorX, enums, string, Uri
Slot, User, Grabber
IButton, IGrabbable
IAssetProvider<T> (materials, textures, etc.)
Stored inline in memory Stored as a pointer/reference
ValueField<T> ReferenceField<T>
ValueWrite<T> ObjectWrite<T>
ValueSource<T> ObjectValueSource<T>
DynamicValueVariable<T> DynamicReferenceVariable<T>

Data Container Components

Resonite has many components that "store data," each serving a different purpose and scope.

Data container scope — from Cloud Variables (broadest) to StoredValue (PF-local)
Data container scope — from Cloud Variables (broadest) to StoredValue (PF-local)

Pure Storage

The simplest containers. A single typed value or reference, no naming, no hierarchy.

ValueField<T>
Stores a single value type. The most basic container. Used by drivers, ProtoFlux, and internal component wiring.
Type string: [FrooxEngine]FrooxEngine.ValueField<bool>
ReferenceField<T>
Stores a reference to another object (slot, component, asset). The reference-type equivalent of ValueField.
Type string: [FrooxEngine]FrooxEngine.ReferenceField<Slot>
Not purely passive: ValueField and ReferenceField have a built-in proxy mechanism — dropping a slot with one of these components onto an inspector input field will source its value into that field. If you need truly inert storage with no inspector side-effects, use ValueTag<T> / ReferenceTag<T> instead. See Resonite-Issues#6288.

Named Variables (Dynamic Variables)

Named, scoped data accessible anywhere within a DynamicVariableSpace. Resonite's primary inter-component communication system.

DynamicVariableSpace
Defines a namespace scope. All DVVs under this slot (in the hierarchy) with matching space name share a namespace.
Type string: [FrooxEngine]FrooxEngine.DynamicVariableSpace
DynamicValueVariable<T>
A named value variable within a space. Has VariableName (string) and Value fields. The workhorse of Resonite data binding.
Type string: [FrooxEngine]FrooxEngine.DynamicValueVariable<string>
DynamicReferenceVariable<T>
Same as DVV but for reference types. Stores a reference to a slot, component, or asset by name.
Type string: [FrooxEngine]FrooxEngine.DynamicReferenceVariable<Slot>

Drivers

Components that continuously drive (overwrite) a target field's value. The target shows a purple indicator when driven.

ValueDriver<T>
Drives a single target field from a source value. One-to-one.
Type string: [FrooxEngine]FrooxEngine.ValueDriver<float>
ValueMultiDriver<T>
Drives multiple target fields from one source. One-to-many.
Type string: [FrooxEngine]FrooxEngine.ValueMultiDriver<float>
ValueCopy<T>
Copies a source field's value to a target field each frame. Like a driver but from an existing field rather than a constant.
Type string: [FrooxEngine]FrooxEngine.ValueCopy<float3>

ProtoFlux Bridge Components

These live on ProtoFlux node slots and bridge between the component data model and ProtoFlux's execution graph.

GlobalReference<T>
A reference from ProtoFlux to a component field. Points to a specific IValue/IField (see Interface Types). Must be manually created when using the API.
Type string: [FrooxEngine]FrooxEngine.ProtoFlux.GlobalReference<...>
GlobalValue<T>
A constant value exposed to ProtoFlux. Unlike GlobalReference, it stores the value directly rather than pointing to another field.
Type string: [FrooxEngine]FrooxEngine.ProtoFlux.GlobalValue<string>
ValueSource<T> / ObjectValueSource<T>
ProtoFlux node that reads a value/reference from the data model. Outputs to other PF nodes.
Type string: [ProtoFluxBindings]...CoreNodes.ValueSource<float3>
ValueWrite<T> / ObjectWrite<T>
ProtoFlux node that writes a value back into the data model. Triggered by an impulse.
Type string: [ProtoFluxBindings]...Nodes.ValueWrite<...>
StoredValue<T> / StoredObject<T>
ProtoFlux-local storage. Persists a value within the PF graph without writing to the data model. Useful for state machines.
Type string: [ProtoFluxBindings]...Nodes.StoredValue<float2>

Persistent variables stored on Resonite's cloud servers. Survive world restarts and can be shared across worlds.

CloudValueVariable<T>
Reads/writes a cloud variable defined in user or group settings. Value persists across sessions.
Type string: [FrooxEngine]FrooxEngine.CloudValueVariable<string>
ActiveUserCloudValueVariable<T>
Like CloudValueVariable but automatically scoped to the active user (the user interacting with it).
Type string: [FrooxEngine]FrooxEngine.ActiveUserCloudValueVariable<bool>
ValueDriver.ValueSource vs ValueSource<T> node. ValueDriver.ValueSource is a member field on the ValueDriver component — you point it at a field ID (e.g. DVV.Value). ValueSource<T> is a separate ProtoFlux node that reads values into a graph via a GlobalReference bridge. Same name, completely different things.

Interface Types

Interfaces are abstract contracts describing capabilities of fields and components. They're the type constraints that determine what you can wire to what. You never create an IValue<T> directly — you create a DynamicValueVariable<T> whose .Value field happens to implement IValue<T>.

Interface Hierarchy

Interface hierarchy — IField, IValue, IGlobalValueProxy and their implementations
Interface hierarchy — IField, IValue, IGlobalValueProxy and their implementations

Core Field Interfaces

Interface What It Means Implemented By Expected By
IField<T> Any typed field on any component. Most general. Every member field ValueDriver.DriveTarget, ValueCopy.Source/Target
IValue<T> A readable typed value. Subset of IField. DVV.Value, ValueField.Value, most typed members ValueDriver.ValueSource, GlobalReference.Reference
IGlobalValueProxy<T> ProtoFlux bridge proxy — wraps IValue for PF nodes. GlobalReference<T> ValueSource.Source, ObjectValueSource.Source, event node inputs
Practical rule: When a field says "expects IValue<T>", drop any .Value field from a DVV or ValueField of matching type T. When it says "expects IField<T>", any typed field from any component works.

Reference-Type Interfaces

These interfaces constrain reference-type fields — you'll see them on ProtoFlux event nodes and interaction components.

Interface What It Constrains Common Concrete Type
IButton Clickable button TouchButton, UIX Button
IGrabbable Grabbable object Grabbable component
IAssetProvider<T> Asset provider of type T PBS_Metallic (Material), StaticTexture2D (Texture), StaticAudioClip (AudioClip)

What Goes in GlobalReference<T>?

The generic parameter of GlobalReference determines what it can point at and which ProtoFlux nodes use it.

GlobalReference<...> Points At Used By PF Node
GlobalReference<IValue<float>> DVV<float>.Value, ValueField<float>.Value ValueSource<float>.Source
GlobalReference<IValue<string>> DVV<string>.Value ObjectValueSource<string>.Source
GlobalReference<IButton> TouchButton, Button component ButtonEvents.Button
GlobalReference<IGrabbable> Grabbable component OnGrabbableGrabbed.Grabbable
GlobalReference<IAssetProvider<AudioClip>> StaticAudioClip component PlayOneShot.Clip
GlobalReference<Slot> Any Slot reference ElementSource<Slot>.Source
API type string gotcha: GlobalReference generic parameters use full assembly-qualified names: GlobalReference<[FrooxEngine]FrooxEngine.IValue<float>>, not shorthand. See API Quick Reference.

Proxies

"Proxy" is an overloaded term in Resonite. Several component families use the name, each serving a different role. They all share the idea of indirection — standing in for something else — but they operate in different contexts.

Proxy Families

Family Components Purpose
ProtoFlux Data Proxies GlobalReference<T>, GlobalValue<T> Bridge between ProtoFlux nodes and the component data model. The most important proxies for data flow.
Inspector / Grab Proxies ValueProxy<T>, ReferenceProxy, AssetProxy<T>, DelegateProxy<T> Make grabbable objects act as values, references, or assets for the in-game inspector's drag-and-drop system.
Lifecycle Proxies DestroyProxy, SaveProxy Redirect destroy or save operations to a different target slot.
ProtoFlux Internal Proxies MethodProxy, FunctionProxy, AsyncMethodProxy, ProtoFluxRefProxy, ProtoFluxGlobalRefProxy, ProtoFluxReferenceProxy, FieldDriveBase+Proxy, ReferenceDrive+Proxy, ... Internal ProtoFlux node machinery (thousands of variants). Not user-facing. Includes nested +Proxy classes that drive nodes create on target slots.
What does +Proxy mean? In .NET, the + symbol denotes a nested class. FieldDriveBase<T>+Proxy is a Proxy class nested inside FieldDriveBase<T>. ProtoFlux drive nodes (like FieldDrive, ReferenceDrive, PlaybackDrive) use these nested proxy components internally — they get placed on the target slot to perform the actual drive operation.

The rest of this section focuses on ProtoFlux Data Proxies, since they are central to understanding how data flows through the system.

ProtoFlux Data Proxies

Why they exist: ProtoFlux runs in its own execution context, separate from the component data model. It needs a standardized interface to read and write fields. Data proxy components provide that bridge — a stable, typed connection point that ProtoFlux nodes can reference.
Component Field ↔ Data Proxy ↔ ProtoFlux Node
Component Field ↔ Data Proxy ↔ ProtoFlux Node

GlobalReference<T> vs GlobalValue<T>

GlobalReference<T>
Points to an existing field. A "window" into the data model. This is the most common data proxy — it doesn't store data itself, it references data that lives elsewhere.
Example: A GlobalReference<IValue<float>> pointing at a DVV<float>'s Value field lets a ValueSource<float> node read that DVV.
GlobalValue<T>
Stores its own constant value. A "constant" exposed to ProtoFlux. No external reference needed — the value lives directly on this component.
Example: A GlobalValue<float> set to 9.81 provides a gravity constant that any ProtoFlux node can read.
IGlobalValueProxy<T> interface: Both GlobalReference<T> and GlobalValue<T> implement IGlobalValueProxy<T>. ProtoFlux node inputs (like ValueSource.Source) expect this interface, which is why either data proxy type can satisfy them.
GlobalReference<T> GlobalValue<T>
Stores A pointer to another field Its own value
Use when Reading/writing dynamic data (DVVs, ValueFields, Grabbables, Buttons) Providing a constant to ProtoFlux
Common with ValueSource, ObjectValueSource, ValueWrite, ObjectWrite, ButtonEvents, OnGrabbableGrabbed Any PF node needing a fixed input

Two mechanisms control field values in Resonite (see also Linkage). They look similar but behave very differently.

Links (cyan, synced) vs Drives (purple, local)
Links (cyan, synced) vs Drives (purple, local)
Link (cyan) Drive (purple)
Indicator Cyan/teal field highlight Purple field highlight
Network sync Yes — value is synced to all users No — computed locally on each client
Persistence Value saved with the world Value NOT saved (recomputed on load)
Who computes The writing client Every client independently
Editable? No (overwritten by link source) No (overwritten by driver)
Set by ValueCopy, ReferenceCopy ValueDriver, ValueMultiDriver, ProtoFlux drives
Use when You want the result synced to everyone You want local computation (animations, UI states)
Rule of thumb: Use links when one client should compute and broadcast the result. Use drives when every client should compute independently (better for animations, less network traffic).

ProtoFlux & the Data Model

ProtoFlux is Resonite's visual programming system. It operates in its own execution context but reads from and writes to the component data model via bridge components.

Data Flow Sequence

Sequence diagram — reading into and writing from ProtoFlux
Sequence diagram — reading into and writing from ProtoFlux
API Gotcha: When you add a ProtoFlux node via the API (addComponent), Resonite does NOT auto-create the companion GlobalReference component. You must manually add it and wire the reference. The in-game node browser does this automatically, but the API doesn't.

The Bridge Pattern

Every ProtoFlux node that reads/writes the data model follows the same pattern:

  1. A GlobalReference<T> component on the node's slot points to a target field
  2. The PF node has a member (e.g. Source, Variable) that references the GlobalReference
  3. At runtime, the node reads/writes through the GlobalReference to the actual field

Data Flow Patterns

Common wiring patterns you'll encounter and create in Resonite.

Pattern 1: DVV → Driver → UI

A DynamicValueVariable drives a UI text element. Change the DVV value, and the text updates automatically.

DVV → ValueDriver → Text component
DVV → ValueDriver → Text component

Pattern 2: DVV → GlobalRef → ProtoFlux Read

ProtoFlux reads a DynamicValueVariable through a GlobalReference bridge.

DVV → GlobalReference → ObjectValueSource → ProtoFlux
DVV → GlobalReference → ObjectValueSource → ProtoFlux

Pattern 3: ProtoFlux → Write → DVV

ProtoFlux writes a computed value back to a DynamicValueVariable.

ProtoFlux → ObjectWrite → GlobalReference → DVV
ProtoFlux → ObjectWrite → GlobalReference → DVV
Remember: string is a value type in FrooxEngine but an object type in ProtoFlux — use ObjectWrite<string> (not ValueWrite<string>) in ProtoFlux. Use ValueWrite for float, bool, int, etc.

Wiring Guide

The previous sections describe what each component is. This section explains how to wire them in the Resonite inspector — what goes in those null fields and what to drag where.

Fields Are the Unit of Reference

The #1 conceptual hurdle: References point to fields (members), not components or slots. You're wiring field-to-field, not component-to-component.

Every field on every component has a unique internal ID used by the data model. When you wire a ValueDriver's ValueSource to a DVV's Value, you're pointing at that specific field — not at the DVV component as a whole.

This means you can wire any field of the correct type to any compatible target, regardless of which component it lives on.

Inspector Color Coding

Field highlight colors tell you what's controlling a field:

Color Meaning Editable?
White (none) Normal, locally set value Yes
Cyan / Teal Linked (ValueCopy, network-synced) No
Purple Driven (ValueDriver, local compute) No

The small circles () next to each field in the inspector are reference handles — grab-and-drop targets. Drag from one ○ and drop onto another to create a reference.

ValueDriver<T> — Step by Step

ValueDriver wiring — Source field → ValueSource, DriveTarget → Target field
ValueDriver wiring — Source field → ValueSource, DriveTarget → Target field
  1. Attach ValueDriver<T> to any slot
  2. ValueSource field: grab the ○ next to the source field (e.g. DVV's Value) and drop it here — expects IValue<T> (see Interface Types)
  3. DriveTarget field: grab the ○ next to the target field (e.g. Text's Content) and drop it here — expects IField<T> (see Interface Types)
  4. The target field turns purple = it's now driven

Common sources for ValueSource: DynamicValueVariable.Value, ValueField.Value, any typed member on any component.

ValueMultiDriver<T> — One to Many

Same as ValueDriver, but DriveTarget is a list. Each entry drives a different target field. Useful for broadcasting one value (e.g. a color) to many receivers.

  1. Set ValueSource to the single source field
  2. Add entries to the DriveTarget list — each entry takes a ○ reference to a target field
  3. All targets turn purple

ValueCopy<T> vs ValueDriver<T>

ValueCopy ValueDriver
Source field type Another existing field (IField<T>) IValue<T> interface
Result color Cyan link (synced, saved) Purple drive (local, not saved)
Network sync Yes — value replicated to all users No — each client computes independently
Use when You want the value synced to all users You want local computation
note

ProtoFlux Wiring — The GlobalReference Bridge

ProtoFlux nodes don't reference data fields directly. They go through a GlobalReference on the same slot.

Reading a Value into ProtoFlux

Wiring: DVV.Value → GlobalReference.Reference → ValueSource.Source → PF Graph
Wiring: DVV.Value → GlobalReference.Reference → ValueSource.Source → PF Graph
  1. The ProtoFlux node (e.g. ValueSource) has a Source field
  2. That field expects a GlobalReference on the same slot
  3. The GlobalReference's Reference field points to the actual data field (e.g. DVV.Value)
  4. In the inspector: drag DVV.Value's ○ → drop onto GlobalReference.Reference

Writing from ProtoFlux

Wiring: PF Graph → ObjectWrite.Variable → GlobalReference.Reference → DVV.Value
Wiring: PF Graph → ObjectWrite.Variable → GlobalReference.Reference → DVV.Value
  1. The ProtoFlux node (e.g. ObjectWrite) has a Variable field (not "Target"!)
  2. Wire VariableGlobalReference on same slot
  3. Wire GlobalReference.Reference → target field (e.g. DVV.Value)

Quick Reference: What to Drop in Each "null" Field

Component Field What to Drop Here Target Turns...
ValueDriver<T> ValueSource Any field of type T (○ handle)
ValueDriver<T> DriveTarget The field you want driven Purple
ValueMultiDriver<T> DriveTarget [n] Each field you want driven Purple
ValueCopy<T> Source Any field of type T
ValueCopy<T> Target The field you want linked Cyan
GlobalReference<T> Reference The data model field to bridge
ValueSource<T> (PF) Source GlobalReference on same slot
ObjectWrite<T> (PF) Variable GlobalReference on same slot

Which Component Do I Need?

Use this decision tree to find the right data component for your use case.

Decision tree flowchart — which data component to use
Decision tree flowchart — which data component to use

.NET Naming Conventions

Resonite is built on .NET (C#). Component type strings follow .NET assembly-qualified naming, but with a Resonite-specific format (see also Complex_Types_in_Components).

Anatomy of a Type String

[FrooxEngine]  FrooxEngine.  DynamicValueVariable  <string>
 ^^^^^^^^^^^   ^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^
 Assembly      Namespace     Class Name             Generic Argument

Assembly Prefixes

Prefix Contains Examples
FrooxEngine Core engine components, UIX, materials, data types BoxMesh, Canvas, PBS_Metallic, DynamicValueVariable
[ProtoFluxBindings] ProtoFlux execution nodes ButtonEvents, ValueWrite, SetLocalPosition

Generic Syntax Rules

Important: Use the Resonite angle-bracket syntax, NOT CLR backtick notation.
Correct (Resonite) Wrong (CLR)
DynamicValueVariable<string> DynamicValueVariable`1[System.String]
ValueField<float3> ValueField`1[BaseX.float3]
ValueField<bool> ValueField`1[System.Boolean]

Common Generic Type Arguments

Value types: bool, int, long, float, double, float2, float3, float4, floatQ, colorX

Reference types: Slot, User

Note: string and Uri are value types in FrooxEngine but object types in ProtoFlux.

Common Gotchas

List members (e.g. MeshRenderer.Materials) do NOT support dot-notation access. Materials.0 fails. Use the list assignment pattern instead.
string and Uri are value types in FrooxEngine (ValueField, DynamicValueVariable) but object types in ProtoFlux (ObjectWrite, ObjectValueSource). Don't look for ValueWrite<string> in ProtoFlux — it doesn't exist.
GlobalReference not auto-created. When adding ProtoFlux nodes via API, you must manually add the GlobalReference<T> companion component and wire it.
ObjectWrite.Variable — the member is called Variable, not Target.

More Resources

Concepts

Key Components

Tools

Community

Interactive Version