Skip to content

Preferences and Feature Flags

SceneryStack simulations expose two different mechanisms for configuring behavior that aren't part of the core interaction: the Preferences dialog, a user-facing settings panel built into every Sim, and query parameters, developer/deployment-facing flags read once at startup. Both exist so features like localization, sound, and accessibility options don't need bespoke UI in every simulation.

The Preferences dialog

Every Sim gets a Preferences button in its navigation bar automatically; what appears inside it is configured by a PreferencesModel passed to Sim:

ts
import { Sim, onReadyToLaunch, PreferencesModel } from 'scenerystack/sim';
import { Property } from 'scenerystack/axon';

const preferencesModel = new PreferencesModel( {
  visualOptions: {
    supportsProjectorMode: true // adds a high-contrast "projector mode" toggle
  },
  audioOptions: {
    supportsSound: true,
    supportsExtraSound: true
  }
} );

onReadyToLaunch( () => {
  const sim = new Sim( new Property( 'My Simulation' ), [ /* screens */ ], {
    preferencesModel
  } );
  sim.start();
} );
PreferencesModel sectionSurfaces
simulationOptionsSim-specific custom preferences you supply yourself
visualOptionsProjector/high-contrast mode, interactive highlights
audioOptionsSound on/off, extra sound, voicing (speech output)
inputOptionsAlternative input customization (e.g. gesture control settings)
localizationOptionsLocale switching, region-and-culture selection

If none of these Options are supplied, the corresponding Preferences tab simply doesn't appear — the dialog only shows tabs relevant to what the simulation actually supports, so passing an empty new PreferencesModel() (the Sim default) yields a minimal dialog.

Query parameters

Query parameters are read once, at launch, from the page URL — they're the mechanism for developer tooling and deployment configuration, not for anything a shipped simulation should let end users toggle from within the running page. Two you'll use constantly during development:

Query parameterEffect
?eaEnables assertions (assert && assert(...) checks throughout SceneryStack) — see Running and Building a Simulation
?locale=frLaunches directly in a given locale, bypassing the Preferences dialog's language picker
?screens=1,2Restricts which screens are available, by 1-based index — useful for testing one screen in isolation

Simulation-specific flags follow the same pattern using QueryStringMachine (from scenerystack/query-string-machine), the schema-validated query parameter parser SceneryStack itself uses internally:

ts
import { QueryStringMachine } from 'scenerystack/query-string-machine';

const myQueryParameters = QueryStringMachine.getAll( {
  // ?showAnswers=true
  showAnswers: {
    type: 'flag'
  },

  // ?initialSpeed=2.5
  initialSpeed: {
    type: 'number',
    defaultValue: 1
  }
} );

if ( myQueryParameters.showAnswers ) {
  // reveal debug-only content
}

QueryStringMachine.getAll validates every parameter against its declared type/defaultValue at startup, so a malformed URL fails fast with a clear error rather than silently misconfiguring the sim.

Query parameters are a developer/QA surface, not an end-user settings UI

It's tempting to add a query parameter as a quick way to make a feature "configurable" and stop there. If end users are meant to change the setting, it belongs in the Preferences dialog (or in-sim UI) instead — query parameters aren't discoverable, aren't documented in-product, and are easy to lose (e.g. on a bookmarked or shared URL that omits them). Reserve query parameters for developer flags, QA/testing switches, and deployment-time configuration.

Where to go next