Skip to content

PhET-iO and Instrumentation

When to read the related pages

This page is the conceptual entry point — what PhET-iO buys you and how Tandem fits in. For runtime dynamic elements and custom IOTypes, read PhET-iO Deep Dive. For day-to-day wiring conventions (what to instrument, how to structure tandems), read PhET-iO Instrumentation Pattern.

PhET-iO is the layer that turns a running simulation into something an external program can inspect, record, and control: every instrumented object's state can be serialized, saved, restored, and set remotely — the same mechanism that powers PhET's state-saving, data-collection, and interoperability features. Instrumentation is opt-in and additive: an uninstrumented simulation runs identically, just without that external surface.

Tandem: the instrumentation identifier

Every instrumentable object — Property, Node, Screen, Emitter, and the PhET-iO base class PhetioObject they build on — takes a tandem option. A Tandem is a hierarchical path name (mirroring where the object lives conceptually, not necessarily in the scene graph) that becomes that object's stable identifier in the PhET-iO API:

ts
import { Tandem } from 'scenerystack/tandem';
import { NumberProperty } from 'scenerystack/axon';

const screenTandem = Tandem.ROOT.createTandem( 'myScreen' );
const modelTandem = screenTandem.createTandem( 'model' );

const massProperty = new NumberProperty( 5, {
  tandem: modelTandem.createTandem( 'massProperty' ),
  phetioDocumentation: 'the mass of the object, in kilograms'
} );
TandemMeaning
Tandem.ROOTThe root of the tandem tree for the whole simulation
Tandem.REQUIREDPlaceholder meaning "this tandem must be supplied by the caller" — used in components designed to always be instrumented
Tandem.OPTIONALPlaceholder meaning "instrumentation is optional here"
someTandem.createTandem( name )Creates a child tandem, extending the hierarchical path

Every Screen requires a tandem, even in a simulation that never enables PhET-iO — see Your First Simulation. Uninstrumented (tandem: Tandem.OPTIONAL, unsupplied) objects simply don't appear in the PhET-iO API; nothing breaks by omitting instrumentation, but nothing is inspectable/settable externally either.

What instrumentation buys you

CapabilityHow
Save/restore full simulation stateEvery instrumented Property's value is captured and can be reapplied later
External controlAnother program can set an instrumented Property's value directly, driving the sim the same as a user interaction would
A documented, stable APIphetioDocumentation and the tandem hierarchy together describe the sim's state surface for tooling and other consumers
Data collection / loggingInstrumented Emitters and state changes can be observed externally without modifying simulation code

IOType: describing serialization

Plain Property<number>/Property<string> instances serialize with the built-in NumberIO/StringIO IOTypes automatically. Custom classes that extend PhetioObject (rather than composing existing instrumented Properties) describe their own serialization with an IOType:

ts
import { IOType, NumberIO } from 'scenerystack/tandem';

type MyThingState = { x: number; y: number };

const MyThingIO = new IOType<MyThing, MyThingState>( 'MyThingIO', {
  valueType: MyThing,
  documentation: 'Serializes the position of a MyThing.',
  toStateObject: myThing => ( { x: myThing.x, y: myThing.y } ),
  applyState: ( myThing, state ) => {
    myThing.x = state.x;
    myThing.y = state.y;
  },
  stateSchema: {
    x: NumberIO,
    y: NumberIO
  }
} );

Most simulations never need to author a custom IOType directly — composing already-instrumented Propertys (each with its own built-in IOType) inside a plain model class covers the overwhelming majority of cases, consistent with the model-view separation pattern where all state lives in Property instances.

Instrument by default, even before you need PhET-iO

Adding tandem/phetioDocumentation after the fact to a model that already has dozens of Properties is far more tedious than threading tandems through from the start. Most PhET simulations instrument their full model tree from day one, whether or not a given release actually ships PhET-iO features — the incremental cost per Property is one option.

Where to go next