PhET-iO Legacy Developer's Guide

After you are familiar with the PhET-iO features and demos at https://phet-io.colorado.edu/, and after you have obtained a license for PhET-iO development, you can use this guide to start developing your own wrappers. A wrapper is an HTML page that embeds a PhET-iO simulation in an iframe and provides added value, such as simulation customization, data logging, dynamic feedback, classroom activities, etc. The wrapper communicates with the simulation iframe using a JavaScript API called SimIFrameClient.js


Getting started

Here is an example wrapper that shows how to connect to a PhET-iO simulation from a wrapper, with hooks for initialization, customization, listening for data streams, etc. You can generate a template like this for a particular simulation by launching the "Studio" wrapper, then pressing the "Generate HTML" button.

<!--
Copyright 2016, University of Colorado Boulder
This PhET-iO file requires a license
USE WITHOUT A LICENSE AGREEMENT IS STRICTLY PROHIBITED.
For licensing, please contact [email protected]
Minimal template for developing a wrapper.  Used as a template for devguide.
@author Sam Reid (PhET Interactive Simulations)
-->
<head>
  <title>PhET-iO Wrapper Harness</title>
</head>
<body>

<!-- Specify the simulation to run in the iframe. -->
<iframe id="sim" width="768" height="464"></iframe>

<!--Load PhET-iO scripts.-->
<script type="text/javascript"
        src="https://phet-io.colorado.edu/sims/faradays-law/1.3.2-phetio/lib/assert.js"></script>
<script type="text/javascript"
        src="https://phet-io.colorado.edu/sims/faradays-law/1.3.2-phetio/lib/SimIFrameClient.js"></script>
<script type="text/javascript"
        src="https://phet-io.colorado.edu/sims/faradays-law/1.3.2-phetio/lib/WrapperUtils.js"></script>
<script type="text/javascript"
        src="https://phet-io.colorado.edu/sims/faradays-law/1.3.2-phetio/lib/QueryStringMachine.js"></script>

<script>

  var sim = WrapperUtils.getSim( 'faradays-law', '1.3.2-phetio' );

  // Construct the sim iframe client that can be used to send messages to the sim.
  var simIFrameClient = new SimIFrameClient( document.getElementById( 'sim' ) );

  // Choose details of how the sim is launched and fill in callbacks for events
  simIFrameClient.launchSim( sim.URL, {

    // Choose whether the sim should emit states and/or input events in addition to the instance-based messages
    // Can be overriden by query parameters in the wrapper phetioEmitStates and phetioEmitInputEvents
    emitStates: false,
    emitInputEvents: false,

    // Callback for messages from the sim's event stream
    phetioEventsListener: function( message ) {
      console.log( 'message emitted: ', JSON.parse( message ) );
    },

    // Callback when the sim is initialized
    onSimInitialized: function() {
      console.log( 'sim initialized' );

      // Send other commands to simIFrameClient as desired, using simIFrameClient.invoke or invokeSequence
    }
  } );
</script>
</body>

Note that this example code is hard-coded to use "faradays-law" version "1.3.2-phetio". If you use this as a starting point for wrapper development, you'll need to replace all occurrences of those strings with the strings appropriate for your simulation and version. Alternatively, you can launch the Studio wrapper and press the "Generate HTML" button to create a template like the one above with customizations provided by the Studio wrapper.

A good place to start is by downloading the example above (or one generated by Studio => "Generate HTML") and launching it on your development machine. Try printing messages to the console from the phetioEventsListener callback. Once that is working, you can start changing the commands sent across the simIFrameClient and switch to a different simulation/version. The links for the SimIFrameClient, WrapperUtils, and simulation itself should be taken from the same version of the simulation, this will ensure they are using a compatible communication protocol.

To send one command to one simulation instance, use simIFrameClient.invoke() as shown in the example above. To invoke multiple commands, use simIFrameClient.invokeSequence(), passing in an argument like startupCustomization in the example above.


Instrumented Instances

Each instrumented instance in a simulation is associated with an identifier (called a phetioID) and a type. The phetioID:

  • appears in messages produced by the object in the data log
  • is used to configure the object
  • is used to interact with the object dynamically

The type describes the nature of the object as well as methods for interacting with the object. The full identifier for an object is given by a dot-delimited string. For example, in Faraday's Law, the magnet's position has the phetioID:

'faradaysLaw.faradaysLawScreen.magnet.position'

Note that all identifiers are prefixed by the name of the simulation and the name of the screen within the simulation to facilitate cross-simulation studies. To identify the names and types of all of the instrumented instances in a simulation, launch the "Studio" wrapper. To see what methods are available for a type, please refer to the "Types" section below.


Query Parameters

Several query parameters govern the behavior of an instrumented PhET simulation:

  • phetioEmitStates: Outputs the full state at the end of every frame.
  • phetioEmitDeltas: Outputs state keyframes every 10 seconds and deltas every frame
  • phetioEmitEmptyDeltas: When emitting states, emit deltas that are empty to simplify playback in some systems.
  • phetioLog: If set to console, this will stream phetioEvents to the console. This is useful for understanding or debugging the phetioEvent log output in real-time. If using Chrome, Firefox, or Safari, using phetioLog=lines provides a colorized single-line output.
  • screens: Choose the screens that will be available in the simulation. Specify the number of the screen (1-indexed) to just get that screen with no home screen, i.e "?screens=2". Specify any number of screen indices separated by a comma "," to add desired screens. For example, if you wanted to run Forces and Motion Basics with only the Net Force screen and the Friction screen, you would use "?screens=1,3". You can also reorder the numbers to change the selection order in the home screen and navbar.

Example Wrappers

When you obtain a license for PhET-iO development, you will receive a password to access the protected wrappers for each PhET-iO simulation. The wrappers demonstrate the PhET-iO functionality of the simulation, provide information for developing wrappers, and provide a starting point for development.


Data Stream

Here is the beginning of a sample data log from the Color Vision simulation:

{
  "messageIndex": 0,
  "eventType": "model",
  "phetioID": "sim",
  "componentType": "SimIO",
  "event": "simStarted",
  "time": 1447285103220,
  "parameters": {
    "sessionID": null,
    "simName": "‪Color Vision‬",
    "simVersion": "1.1.0-phet-io.3",
    "url": "https://phet-dev.colorado.edu/html/phet-io/newschools/color-vision_en.html",
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko)
    Chrome/46.0.2490.80 Safari/537.36",
    "provider": "PhET Interactive Simulations, University of Colorado Boulder"
  }
}
{
  "messageIndex": 1,
  "eventType": "user",
  "phetioID": "homeScreen.singleBulbScreenLargeButton",
  "componentType": "PushButtonIO",
  "event": "fired",
  "time": 1447285111392,
  "children": [
    {
      "messageIndex": 2,
      "eventType": "model",
      "phetioID": "sim.showHomeScreen",
      "componentType": "PropertyIO",
      "event": "changed",
      "time": 1447285111393,
      "parameters": {
        "oldValue": true,
        "newValue": false
      }
    }
  ]
}
{
  "messageIndex": 3,
  "eventType": "user",
  "phetioID": "colorVision.singleBulbScreen.photonRadioButton",
  "componentType": "RadioButtonIO",
  "event": "fired",
  "time": 1447285114104,
  "parameters": {
    "value": "photon"
  },
  "children": [
    {
      "messageIndex": 4,
      "eventType": "model",
      "phetioID": "colorVision.singleBulbScreen.beamType",
      "componentType": "PropertyIO",
      "event": "changed",
      "time": 1447285114104,
      "parameters": {
        "oldValue": "beam",
        "newValue": "photon"
      }
    }
  ]
}

Here is a brief description of each of the fields in a message:

Attribute Description
messageIndex The number of the message, starting with 0. Can be used to verify that the correct number of messages was received.
eventType {user|model} whether the message was originated by a user action or by the simulation itself. For instance, a button press is a user event while a state change in the simulation is a model event (even if it was triggered by a user event).
phetioID The unique identifier for the object sending the message. It usually starts with ${simulation-name}.${screen-name} except for globals outside of screens or objects in the navigation bar.
componentType The name of the component type of the object that sent the message. Component type names begin with a capital letter T because they refer to Type wrappers.
event The name of the event that was fired by the object.
time The number of milliseconds since the epoch (in 1970) as reported by the client machine. No effort is made to verify that this matches any other clock.
parameters (optional) Key value pairs that describe the details of the event.
children (optional) Events that were triggered while this event was being processed.

The events are delivered as JSON objects and must be parsed using a JSON parser. Since JSON and JavaScript do not guarantee the order of the fields, the data stream cannot be parsed as simple text under the assumption that fields appear in a deterministic ordering.


iframe API

The iframe API can be used to communicate with a running simulation. Methods can be called on any of the simulation instances. Commands can be sent to them, or values retrieved from them. Here's a full example of how to use the sim iframe API to toggle whether a simulation is active (running) or inactive (frozen), which also demonstrates how to send commands to the simulation and receive messages back from the simulation.

// Get a reference to the iframe for the sim
var simFrame = document.getElementById( 'sim' );

// This sim does not need any listeners to be added before the sim launches,
// so it can be phetioStandalone
simFrame.src = sim.URL + '&phetioStandalone';

// Construct the sim iframe client that can be used to send messages to the sim.
var simIFrameClient = new SimIFrameClient( simFrame );

// Keep track of whether the sim is active or not
var lastActiveValue = true;

// When the user presses the "Toggle Active" button, send a command to the iframe
document.getElementById( 'toggleActiveButton' ).addEventListener( 'click', function() {

  // Send a message using the simIFrameClient to invoke a method on an instance
  simIFrameClient.invoke( sim.camelCaseName + '.activeProperty', 'setValue', [ !lastActiveValue ] );
} );

// When the sim launches, wire up a listener to the active Property,
// so we can report its value and use the correct value when toggling
simIFrameClient.onSimInitialized( function() {

  // Send a message using the simIFrameClient to link up to the active Property
  simIFrameClient.invoke( sim.camelCaseName + '.activeProperty', 'link', [ function( active ) {
    lastActiveValue = active;
    document.getElementById( 'readout' ).innerHTML = 'Active: ' + active;
  } ] );
} );

There are two ways to pass a function across the API. One runs the function in the parent frame and the other runs the function in the simulation frame. In the above example, the args has a function wrapped in an array. This will run in the parent frame. If passing an object literal with {phetioID, method, args}, it will be invoked synchronously in the simulation frame. See the source for state.html for an example.

Startup Sequence

To prevent missing any events sent from the simulation to the wrapper (in the case of multi-threaded browser support for multiple frames), PhET-iO provides a startup sequence with a step where the wrapper can register any listeners. This happens before the simulation launches. This pattern is implemented in the example provided above. Here is an overview of the startup sequence:

  1. The wrapper starts up and initializes its state.
  2. The wrapper creates a listener with "window.addEventListener( 'message',...)".
  3. The wrapper sets the source of the simulation iframe.
  4. The simulation preload files initialize (including phet-io.js and related files).
  5. The simulation creates a listener with "window.addEventListener( 'message',...)".
  6. The simulation sends a postMessage indicating that it is ready for PhET-iO messages.
  7. The wrapper receives the postMessage and sends a composite command to the simulation frame adding any desired listeners, setting any desired startup configuration, etc., and ending with the directive to start launching the iframe portion of the simulation. Alternatively, this could be a sequence of individual messages, but the wrapper would have to wait for the response message for each before sending the final go-ahead to launch the remainder of the simulation.
  8. The simulation receives the message to launch the iframe portion and finishes launching normally.

// Wait for the phase where we can add listeners
simIFrameClient.onPhETiOInitialized( function() {

  // Add any desired listeners to phet-io (cannot communicate with sim instances at this
  // point since they have not been created yet
  simIFrameClient.invoke( 'phetio', 'addPhETIOEventsListener', [ function( message ) {
      textarea.innerHTML += '\n' + message;
      textarea.scrollTop = textarea.scrollHeight;
    } ], function() {

      // after that is complete, launch the simulation
      simIFrameClient.invoke( 'phetio', 'launchSimulation', [] );
    }
  );
} );
          
        

Types

Each instrumented instance in a PhET-iO simulation has a type which provides a set of methods which can be called from query parameters or across the iframe API. The events field declares which event names can be emitted by the type in the phetioEvents log. The "IO" suffix indicates that it is a PhET-iO Type instead of the actual implementation simulation type. This section provides documentation for all instrumented types, including the events that can appear in the data stream, and the methods associated with each type.

AccordionBoxIO (extends NodeIO)
A traditional accordionBox
events: expanded,collapsed
AquaRadioButtonIO (extends RadioButtonIO)
A radio button which looks like the Mac "Aqua" radio buttons
events: fired
setCircleButtonVisible: (BooleanIO) ➞ VoidIO
Sets whether the circular part of the radio button will be displayed.
ArrayIO (extends ObjectIO)
A wrapper for the built-in JS array type, with the element type specified.
BarrierRectangleIO (extends NodeIO)
Shown when a dialog is present, so that clicking on the invisible barrier rectangle will dismiss the dialog
events: fired
BooleanIO (extends ObjectIO)
Wrapper for the built-in JS boolean type (true/false)
Bounds2IO (extends ObjectIO)
a 2-dimensional bounds rectangle
Bounds3IO (extends ObjectIO)
a 3-dimensional bounds (bounding box)
ButtonListenerIO (extends ObjectIO)
Button listener
events: up,over,down,out,fire
CheckboxIO (extends NodeIO)
A traditional checkbox
events: toggled
isChecked: () ➞ BooleanIO
Returns true if the checkbox is checked, false otherwise
link: (FunctionIO) ➞ VoidIO
Link a listener to the underlying checked PropertyIO. The listener receives an immediate callback with the current value (true/false)
setChecked: (BooleanIO) ➞ VoidIO
Sets whether the checkbox is checked or not
ColorIO (extends ObjectIO)
A color, with rgba
ComboBoxIO (extends NodeIO)
A traditional combo box
events: fired,popupShown,popupHidden
ComboBoxItemNodeIO (extends NodeIO)
A traditional item node for a combo box
events: fired
DerivedPropertyIO (extends ObjectIO)
Like PropertyIO, but not settable. Instead it is derived from other DerivedPropertyIO or PropertyIO instances
events: changed
getValue: () ➞ ObjectIO
Gets the current value
link: (FunctionIO) ➞ VoidIO
Adds a listener which will receive notifications when the value changes and an immediate callback with the current value upon linking.
unlink: (FunctionIO) ➞ VoidIO
Removes a listener that was added with link
DialogIO (extends PanelIO)
A dialog panel
EventsIO (extends ObjectIO)
Event system, with multiple channels defined by string keys
events: documentation,fromStateObject,typeName,methods,supertype,getMethodDeclaration,allMethods
addListener: (StringIO,FunctionIO) ➞ VoidIO
Adds a listener to the specified event channel
removeListener: (StringIO,FunctionIO) ➞ VoidIO
Removes a listener that was added with addListener
FaucetNodeIO (extends NodeIO)
Faucet that emits fluid, typically user-controllable
events: startTapToDispense,endTapToDispense
FocusIO (extends ObjectIO)
PhET-iO Type for the instance in the simulation which currently has keyboard focus.
FontIO (extends ObjectIO)
Font handling for text drawing. Options:
style: normal // normal | italic | oblique
variant: normal // normal | small-caps
weight: normal // normal | bold | bolder | lighter | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
stretch: normal // normal | ultra-condensed | extra-condensed | condensed | semi-condensed | semi-expanded | expanded | extra-expanded | ultra-expanded
size: 10px // absolute-size | relative-size | length | percentage -- unitless number interpreted as px. absolute suffixes: cm, mm, in, pt, pc, px. relative suffixes: em, ex, ch, rem, vw, vh, vmin, vmax.
lineHeight: normal // normal | number | length | percentage -- NOTE: Canvas spec forces line-height to normal
family: sans-serif // comma-separated list of families, including generic families (serif, sans-serif, cursive, fantasy, monospace). ideally escape with double-quotes

FunctionIO (extends ObjectIO)
Wrapper for the built-in JS function type
HSliderIO (extends NodeIO)
A traditional slider component, with a knob and possibly tick marks
setMajorTicksVisible: (BooleanIO) ➞ VoidIO
Set whether the major tick marks should be shown
setMinorTicksVisible: (BooleanIO) ➞ VoidIO
Set whether the minor tick marks should be shown
HSliderTrackIO (extends NodeIO)
The track for a knob of a traditional slider
JoistButtonIO (extends NodeIO)
The buttons used in the home screen and navigation bar
events: fired
MenuItemIO (extends NodeIO)
The item buttons shown in a popup menu
events: fired
NavigationBarScreenButtonIO (extends NodeIO)
A pressable button in the simulation's navigation bar
events: fired
NodeIO (extends ObjectIO)
The base type for graphical and potentially interactive objects
addPickableListener: (FunctionIO) ➞ VoidIO
Adds a listener for when pickability of the node changes
addVisibleListener: (FunctionIO) ➞ VoidIO
Adds a listener for when visibility of the node changes
isPickable: () ➞ BooleanIO
Gets whether the node is pickable (and hence interactive)
isVisible: () ➞ BooleanIO
Gets a Boolean value indicating whether the node can be seen and interacted with
setOpacity: (NumberIO) ➞ VoidIO
Set opacity between 0-1 (inclusive)
setPickable: (BooleanIO) ➞ VoidIO
Set whether the node will be pickable (and hence interactive)
setRotation: (NumberIO) ➞ VoidIO
Set the rotation of the node, in radians
setVisible: (BooleanIO) ➞ VoidIO
Set whether the node will be visible (and interactive)
NumberIO (extends ObjectIO)
Wrapper for the built-in JS number type (floating point, but also represents integers)
NumberControlIO (extends NodeIO)
A numeric readout with a background
ObjectIO
The root of the wrapper object hierarchy
ObservableArrayIO (extends ObjectIO)
An array that sends notifications when its values have changed.
events: itemAdded,itemRemoved
OnOffSwitchIO (extends NodeIO)
A traditional switch component
events: toggled
OptionsDialogIO (extends DialogIO)
A dialog panel
PanelIO (extends NodeIO)
A container for other NodeIOs
ParticleIO (extends ObjectIO)
The model for a single particle such as an electron, proton, or neutron.
ParticleAtomIO (extends ObjectIO)
A model of an atom that tracks and arranges the subatomic particles, i.e. protons, neutrons, and electrons, of which it is comprised. When particles are added, they are moved into the appropriate places. This object also keeps track of things like atomic number, mass number, and charge.
PhETIO (extends ObjectIO)
Mediator for the phet-io module, with systemwide methods for communicating with the sim or other globals
events: simStarted,state,stateDelta,frameCompleted,stepSimulation,inputEvent,displaySize
addInstanceAddedListener: (FunctionIO) ➞ VoidIO
Adds a listener that receives a callback whenever a new sim instance has been prepared for interoperability
addInstanceRemovedListener: (FunctionIO) ➞ VoidIO
Removes a listener that was added with addInstanceAddedListener
addPhETIOEventsListener: (FunctionIO) ➞ VoidIO
Adds a listener to the phetioEvents event channel, for data analysis
endEvent: (NumberIO) ➞ VoidIO
End a message
getPhETIOIDs: () ➞ ArrayIO
Gets a list of all of the wrapped instances which are available for interoperability.
getRandomSeed: () ➞ NumberIO
Get the random seed, used for replicable playbacks
getState: () ➞ ObjectIO
Gets the full state of the simulation
getValues: (ArrayIO) ➞ ObjectIO
Get the current values for multiple Property/DerivedProperty at the same time. Useful for collecting data to be plotted, so values will be consistent.
invokeInputEvent: (StringIO) ➞ VoidIO
Plays back a recorded input event into the simulation.
isInteractive: () ➞ BooleanIO
Gets whether the sim can be interacted with (via mouse/touch).
launchSimulation: () ➞ VoidIO
Finish launching the simulation, called from a wrapper after all cross-frame initialization is complete
rasterizeDisplay: (FunctionIO) ➞ VoidIO
Rasterize the display, asynchronously
setDisplaySize: (NumberIO,NumberIO) ➞ VoidIO
set the size of the visible region for the simulation
setInteractive: (BooleanIO) ➞ VoidIO
Sets whether the sim can be interacted with (via mouse/touch).
setPlaybackMode: (BooleanIO) ➞ VoidIO
Disable the sim clock driver so that time will only pass as it is played back from a log file.
setRandomSeed: (NumberIO) ➞ VoidIO
Set the random seed, used for replicable playbacks
setSimStartedMetadata: (ObjectIO) ➞ VoidIO
Set additional data that is added to the simStarted event.
setState: (ObjectIO) ➞ VoidIO
Sets the full state of the simulation
simulateError: () ➞ VoidIO
Simulate an error for testing purposes
startEvent: (ObjectIO) ➞ NumberIO
Begin a message
stepSimulation: (NumberIO) ➞ VoidIO
Steps one frame of the simulation.
triggerEvent: (ObjectIO) ➞ VoidIO
Start and end a message
PhetMenuIO (extends NodeIO)
The PhET Menu in the bottom right of the screen
PropertyIO (extends ObjectIO)
Model values that can send out notifications when the value changes. This is different from the traditional observer pattern in that listeners also receive a callback with the current value when the listeners are registered.
events: changed
getValue: () ➞ ObjectIO
Gets the current value.
link: (FunctionIO) ➞ VoidIO
Add a listener which will be called when the value changes. The listener also gets an immediate callback with the current value.
setValue: (ObjectIO) ➞ VoidIO
Sets the value of the property, and triggers notifications if the value is different
unlink: (FunctionIO) ➞ VoidIO
Removes a listener
PushButtonIO (extends NodeIO)
A pressable button in the simulation
events: fired
addListener: (FunctionIO) ➞ VoidIO
Adds a listener that is called back when the button is pressed.
fire: () ➞ VoidIO
Performs the action associated with the button
RadioButtonIO (extends NodeIO)
A traditional radio button
events: fired
RadioButtonGroupMemberIO (extends NodeIO)
A traditional radio button
events: fired
RandomIO (extends ObjectIO)
Generates pseudorandom values
ScreenIO (extends ObjectIO)
A single screen for a PhET simulation
ScreenButtonIO (extends NodeIO)
A pressable button in the simulation, in the home screen
events: fired
SimIO (extends ObjectIO)
The type for the simulation instance
events: simStarted
addEventListener: (StringIO,FunctionIO) ➞ VoidIO
Add an event listener to the sim instance
getScreenshotDataURL: () ➞ StringIO
Gets a base64 representation of a screenshot of the simulation as a data url
phetioCommandProcessor (extends ObjectIO)
Instance of a phetioCommandProcessor.
events: invoked
SphereBucketIO (extends ObjectIO)
A model of a bucket into which spherical objects can be placed.
StringIO (extends ObjectIO)
Wrapper for the built-in JS string type
TandemIO (extends ObjectIO)
Tandems give sim elements their phet-io identifiers
EmitterIO (extends ObjectIO)
Emitters indicate when events have occurred, with optional arguments describing the event
events: emitted
addListener: (FunctionIO) ➞ VoidIO
Add a listener which will be called when the emitter emits.
TandemSimpleDragHandlerIO (extends ObjectIO)
Drag listener for objects that can be dragged by the user.
events: dragStarted,dragged,dragEnded
TextIO (extends NodeIO)
PhET-iO Type the scenery Text node
events: textChanged
addTextChangedListener: (FunctionIO) ➞ VoidIO
Add a listener for when the text has changed.
getFontOptions: () ➞ FontIO
Get font options for this TextIO instance as an object
getMaxWidth: () ➞ NumberIO
Get maximum width of text box in px
getText: () ➞ StringIO
Get the text content
setFontOptions: (FontIO) ➞ VoidIO
Set font options for this TextIO instance, e.g. {size: 16, weight: bold}
setMaxWidth: (NumberIO) ➞ VoidIO
Set maximum width of text box in px. If text is wider than maxWidth at its default font size, it is scaled down to fit.
setText: (StringIO) ➞ VoidIO
Set the text content
ToggleButtonIO (extends NodeIO)
A button that toggles state (in/out) when pressed
events: toggled
Vector2IO (extends ObjectIO)
A numerical object with x/y scalar values
Vector3IO (extends ObjectIO)
Basic 3-dimensional vector, represented as (x,y,z)
VerticalCheckboxGroupIO (extends NodeIO)
A vertical group of checkboxes
VoidIO (extends ObjectIO)
Type for which there is no instance, usually to mark functions without a return value
WavelengthSliderIO (extends NodeIO)
A slider that shows wavelengths for selection

Errors

phetioCommandProcessor Invocation Error
Thrown when a SimIFrameClient.invoke() call causes an internal sim error. In general this is best to report back to the PhET-iO team.

Tips and Tricks


Reset Issues

If you are planning to customize a simulation with a Reset All button, then special action must be taken. Resetting normally will remove the customizations, restoring the sim to its original state. If you don't need the Reset All button, then you can hide it using the API for NodeIO.setVisible(). Otherwise, you will need to add a listener to the Reset All button to apply the customizations on the button fire event. This could be done successfully in SimIFrameClient.onSimInitialized(). Note that you can add SimIFrameClient callbacks from the launchSim options.

var sim = WrapperUtils.getSim( 'faradays-law', '1.3.2-phetio' );

// customizations to be added to the sim on startup.
var expressions = [
  {
    phetioID: 'faradaysLaw.faradaysLawScreen.model.magnetModel.showFieldLinesProperty',
    method: 'setValue',
    args: [ true ]
  },
  {
    phetioID: 'faradaysLaw.faradaysLawScreen.model.showSecondCoilProperty',
    method: 'setValue',
    args: [true]
  }
];

simIFrameClient.launchSim( sim.URL, {
  onSimInitialized: function() {
   simIFrameClient.invokeSequence(expressions);

   // When the sim is reset, re-apply the initial customization.
   simIFrameClient.invoke(
    'faradaysLaw.faradaysLawScreen.view.controlPanel.resetAllButton',
    'addListener',
    [ function() {
      simIFrameClient.invokeSequence( expressions );
    } ] );
   }
} );