PhET-iO Wrappers for friction 1.5.0-dev.12

A PhET-iO wrapper embeds a PhET-iO simulation and leverages the PhET-iO API and feature set. The following wrappers demonstrate the PhET-iO API and features for this simulation, and can be used as examples to begin new wrapper development.

Wrapper Description
Simulation Shows the simulation without any data streams or iframes.
Studio Shows all of the instrumented instances in a simulation, with their corresponding IDs, types and a basic functionality for testing interacting with them. This also provides a "Generate HTML" feature which can be used to create a customized simulation or to generate a template/starting point for developing new wrappers.
Events: colorized Shows the PhET-iO Event Stream in the console, colorized and optimized for human readability. The console must be open to see this data. Chrome/Firefox/Safari only.
Events: JSON Shows the PhET-iO Event Stream in the console, formatted in JSON, suitable for machine parsing and analysis. The console must be open to see this data.
Events: textarea Shows the PhET-iO Event Stream in the wrapper frame instead of the console.
Events: recording The simulation is embedded in a full-size iframe and emits events to the console. Query parameters can be added to redirect the data stream to another location.
Events: playback Plays back a previously recorded session (make sure to use the same browser as was used for recording to rule out platform-specific issues).
State Shows a JSON object representing the simulation's state, and immediately sets the state to a copy of the simulation for testing the ability to set state to a simulation. Provides a "Launch" button to launch another copy of the simulation with the given state using Query Parameters.
Mirror Inputs Shows the Scenery input event stream in the console and mirrors the events to a copy of the simulation, to test for visual playback (like a recorded screen capture).
Screenshot Shows a "Screenshot" button that can be used to capture and display screenshots from a live simulation.
Active Shows a button that enables the user to toggle whether a simulation is active (running and taking user input) or inactive (paused and not accepting user input).

API

Here is a basic example of a functional wrapper, demonstrating some PhET-iO features.

<!DOCTYPE HTML>
<!--
Copyright 2016-2018, University of Colorado Boulder
This PhET-iO file requires a license
USE WITHOUT A LICENSE AGREEMENT IS STRICTLY PROHIBITED.
For licensing, please contact phethelp@colorado.edu
Minimal template for developing a wrapper.  Used as a template for devguide.
@author Sam Reid (PhET Interactive Simulations)
@author Michael Kauzmann (PhET Interactive Simulations)
-->
<html>
<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 src="{{PHET_IO_LIB_ABSOLUTE_PATH}}"></script>

<script>

// Construct the sim iframe client that can be used to interface between the sim. The simIFrameClient can send messages
// and receive messages as changes occur in the sim.
var simIFrameClient = new phetio.SimIFrameClient( document.getElementById( 'sim' ) );

// Start the simulation with specific customizations, specified below.
simIFrameClient.launchSim( {

  /**
   *  Choose details of how the sim is launched and fill in callbacks for events
   */

  // Data stream customizations
  // Choose whether the sim should emit json states and/or input events in addition to the instance-based messages.
  // This can be overriden by query parameters in the wrapper.
  emitStates: false,
  emitInputEvents: false,

  // NOTE: For development only
  // This will launch the simulation with added error throwing to catch mistakes and problems easier.
  debug: true,

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

  // Add a listener when PhET-iO is ready, before the simulation has started initialization
  onPhETiOInitialized: function() {

    // add a listener called whenever an instance is created in the simulation, print out that created phetioID
    simIFrameClient.invoke( 'phetio', 'addInstanceAddedListener', [
      function( phetioID ) {
        console.log( phetioID + ' has been created' );
      } ] );
  },

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

    // Invoke a method on a variable in the simulation. Open the "Studio" wrapper to see all of the variables
    // and methods available in friction 1.5.0-dev.12
    simIFrameClient.invoke( 'phetio', 'getState', [], function( state ) {
      console.log( 'got the simulation state: ' + JSON.stringify( state, null, 2 ) );
    } );
  },

  // Callback when the sim encounters an error
  onError: function( error ) { throw error; }
} );
</script>
</body>
</html>

This file can be used as a template for beginning your own wrapper development. Alternatively, you can launch the Studio wrapper above 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. All necessary globals like SimIFrameClient, are imported from the phet-io.js import. To assure compatibility, please make sure that the version and sim from that import match that of the sim you are loading.

To send one command to one simulation instance, use simIFrameClient.invoke() as shown in the example above. To invoke multiple commands, use simIFrameClient.invokeSequence(), shown in an example later in this page.

Wrapper development

When developing wrappers, there are a few tools that can make the process easier.

These options are only recommended for development. When moving to production ready software, the onError callback may still be useful, but the debug option and the phetioThrowSimErrors query parameter may cause unwanted problems in production code. For a complete list of options that can be passed to SimIFrameClient.launchSim(), as well as a list of all valid query parameters both for the wrapper and simulation frames, see the Full API Documentation.

Further documentation

For more documentation detailing globals imported in the phet-io.js library, phet-io and simulation query parameters, and more, please see the Full API Documentation.

Supporting Customization and Reset

If you are planning to customize a simulation with a Reset All button, then special action must be taken. Resetting can remove certain 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 with NodeIO.setVisible(). If the ResetAllButton is made invisible, invoking customizations on the simulation in the SimIFrameClient.onSimInitialized() listener is enough. Otherwise, you will need to add a listener to the Reset All button to apply the customizations on the button fire event. This can also be done successfully in SimIFrameClient.onSimInitialized(). Note that it is recommended that you add SimIFrameClient callbacks (like onSimInitialized and onPhetioInitialized) from the launchSim options.


// Note that this example is for the Faraday's Law PhET-iO Simulation

// customizations to be added to the sim on startup. Here we want both coils and the field lines displayed.
var customizations = [
  {
    phetioID: 'faradaysLaw.faradaysLawScreen.model.magnetModel.showFieldLinesProperty',
    method: 'setValue',
    args: [ true ]
  },
  {
    phetioID: 'faradaysLaw.faradaysLawScreen.model.showSecondCoilProperty',
    method: 'setValue',
    args: [true]
  }
];

simIFrameClient.launchSim( {
  onSimInitialized: function() {
   simIFrameClient.invokeSequence(customizations);

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