The SpringRoll Container is an <iframe>
controller for interacting with SpringRoll applications hosted locally or in SpringRoll Connect.
The SpringRoll Container is available on npm. Install it with
npm install --save springroll-container
Here's a handful of libraries and terms that we'll use throughout this document:
- SpringRoll an JavaScript library for building portable and accessible HTML5 games
- Bellhop an event-based wrapper around the
postMessage
API - SpringRollConnect a content-management system for SpringRoll games. The SpringRoll Container can interact with a SpringRollConnect server to embed a game that is hosted remotely.
The following examples assume that you've created an HTML page with an iframe on it:
<iframe id="game" scrolling="no"></iframe>
and that you've imported this module:
import { Container } from 'springroll-container';
const container = new Container('#game');
container.openPath('local/path/to/game.html');
const container = new Container('#game');
container.openLocal('https://example.com/path/to/game.html');
const container = new Container('#game');
container.openRemote('https://springroll-connect.example.com/api/release/game-slug');
import { PausePlugin, Container } from 'springroll-container';
const container = new Container('#game', {
plugins: [
// Assuming that there is a <button id="pause-button" /> on the page somewhere
new PausePlugin('button#pause-button'),
]
});
container.openPath('path/to/game.html');
In some cases, you may have an existing HTMLIframeElement rather than a CSS selector. You can also instantiate a container from the DOM element as well:
import { Container } from 'springroll-container';
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
const container = new Container(iframe);
container.openPath('/path/to/game.html');
This section provides instructions on how to use the built-in plugins for SpringRoll Container. For writing or updating older plugins, see the Plugin Authoring Guide.
The Container has several built-in plugins that allow the user to control various aspects of a game/application.
These are initialized with either a query selector string (similar to what you would pass to document.querySelector)
or an HTMLElement
.
Plugins in the SpringRollContainer correspond to a matching feature in SpringRoll Core.
If the container has a plugin enabled corresponding to a feature that the game doesn't contain, the container will automatically hide the corresponding UI element.
For example, if the container has the CaptionsTogglePlugin
enabled with a corresponding button to toggle captions but the game doesn't actually have captions, the container will hide the captions toggle button automatically.
import { PausePlugin, HelpPlugin, Container } from 'springroll-container';
const container = new Container("#game", {
plugins: [
new PausePlugin('button#pause-button'), // Pauses or unpauses the game
new HelpPlugin('button#help'), // requests a hint or help from the game
]
});
container.openPath('game.html');
PausePlugin
sets a className of 'paused' or 'unpaused' on individual pause buttons.
The PausePlugin
also has an optional flag
manageOwnVisibility
. This defaults to true
and is intended to disable the visibility management of the plugin for environements
that handle visibility/focus themselves. For most web based contexts this flag will not be needed.
import { PausePlugin, Container } from 'springroll-container';
const container = new Container("#game", {
plugins: [
// manageOwnVisibility is set to false which means the plugin will not send pause or unpause events if the app is no longer
// focused or other similar states (switched tabs, etc)
new PausePlugin('button#pause-button', false),
]
});
container.openPath('game.html');
There are two plugins that interact with captions: CaptionsTogglePlugin
, and CaptionsStylePlugin
.
CaptionsTogglePlugin
allows the user to hide or show the captions in the game.
CaptionsStylePlugin
allows the user to control the size, placement, and color of the captions.
import { CaptionsStylePlugin, CaptionsTogglePlugin, Container } from 'springroll-container';
const container = new Container('#game', {
plugins: [
new CaptionsTogglePlugin('#captions'),
new CaptionsStylePlugin(
// expects exactly three(3) radio buttons with values "small", "medium", and "large" indicating caption font sizes.
'input[name=captions-font-size]',
// expects exactly two(2) radio buttons with values "default" (black background, white text),
// and "inverted" (black text, white background) for caption color schemes
'input[name=captions-font-color]',
// expects exactly two(2) radio buttons values "top" and "bottom".
// Indicating that captions should be placed at the top or bottom of the screen.
'input[name=captions-alignment]',
),
]
});
container.openPath('game.html');
Typical HTML for powering the captions plugin might look like this:
<button id="captions">Toggle Captions</button>
<div>
<label><input name="captions-font-size" value="sm" /> Small</label>
<label><input name="captions-font-size" value="md" /> Medium</label>
<label><input name="captions-font-size" value="lg" /> Large</label>
</div>
<div>
<label><input name="captions-font-color" value="default" /> Default</label>
<label><input name="captions-font-color" value="inverted" /> Inverted</label>
</div>
<div>
<label><input name="captions-alignment" value="top" /> Default</label>
<label><input name="captions-alignment" value="bottom" /> Inverted</label>
</div>
Note that the captions plugin sets a class of muted
or unmuted
on the caption buttons as they are toggled.
The sound plugin allows users to control the volume of individual audio channels within the game. SpringRoll supports three audio channels: VO, SFX, and Music and we encourage developers to use them as it empowers users to customize their game play to suite their needs.
The sound plugin supports a total of eight controls:
- A global sound mute
- Mute buttons for each of the three audio channels mentioned above - VO, SFX, Music
- A global sound volume slider
- Volume sliders for each of the three audio channels mentioned above - VO, SFX, Music
import { SoundPlugin, Container } from 'springroll-container';
const container = new Container('#game', {
plugins: [
new SoundPlugin({
soundButtons: '#soundButton', // mutes or unmutes all game audio
musicButtons: '#musicButton', // mutes or unmutes the music
voButtons: '#voButton', // mutes or unmutes the voice over
sfxButtons: '#sfxButton', // mutes or unmutes the game's sound effects
// The sound button expects these to be HTML range inputs
soundSliders: '#soundSlider', // controls the game's audio volume
musicSliders: '#musicSlider', // controls the game's music volume
voSliders: '#voSlider', // controls the game's voice-over volume
sfxSliders: '#sfxSlider', // controls the game's sound effects volume
}),
]
});
container.openPath('game.html');
SoundPlugin
will set a class of muted
or unmuted
on each button as they are toggled
Mechanics are various configurable aspects of the game that can determine how a user plays the game (see table below for details). Some games will support many of these features, some none at all. We doubt one game will use all of them though.
Each plugin is responsible for one of the listed mechanics and should be provided a HTML range input, and an optional default value. Each mechanic's value will range between 0 to 1, and the default initial value is always 0.5.
import {
HitAreaScalePlugin, DragThresholdScalePlugin, HealthPlugin, ObjectCountPlugin,
CompletionPercentagePlugin, SpeedScalePlugin, TimersScalePlugin, InputCountPlugin,
Container
} from 'springroll-container';
const container = new Container('#game', {
plugins: [
new HitAreaScalePlugin('#hitAreaScaleSlider', {defaultHitAreaScale = 0.5}),
new DragThresholdScalePlugin('#dragThresholdScaleSlider', {defaultDragThresholdScale = 0.5}),
new HealthPlugin('#healthSlider', {defaultHealth = 0.5}),
new ObjectCountPlugin('#objectCountSlider', {defaultObjectCount = 0.5}),
new CompletionPercentagePlugin('#completionPercentageSlider', {defaultCompletionPercentage = 0.5}),
new SpeedScalePlugin('#speedScaleSlider', {defaultSpeedScale = 0.5}),
new TimersScalePlugin('#timersScaleSlider', {defaultTimersScale = 0.5}),
new InputCountPlugin('#inputCountSlider', {defaultInputCount = 0.5}),
]
});
container.openPath('game.html');
MechanicsPlugin Options:
See the SpringRoll Application Docs for more detailed information.
Feature | Key | Description |
---|---|---|
Hit Area Scale | hitAreaScale | Allows the player to define how large or small they want hit boxes for clicking/touching to be in the game. Gives the player the ability to make elements easier or harder to hit. |
Drag Threshold Area | dragThresholdArea | Allows the player to define how sensitive they want object drag detection to be. More or less sensitivity can make certain game challenges more completable for players |
Health | health | Allows the player to define how many attempts, retries, lives, or health they have in the game. |
Object Count | objectCount | Allows the player to define how many objects, hidden or otherwise, are required to complete objectives throughout gameplay. |
Speed Scale | speedScale | Allows the player to define how quickly or slowly game mechanics move. |
Completion Percentage | completionPercentage | Allows the player to define what percentage of a task is required to be finished to complete that task. |
Timer Scale | timerScale | Allows the player to adjust timers in game to give more or less time to complete tasks. |
Input Count | inputCount | Allows the player to define how many clicks, taps, or keyboard inputs are required to complete objectives. |
The Button Size plugin allows users to control the size of buttons within the game. The size value ranges from 0 to 1, defaulting to 0.5.
The Pointer Size plugin allows users to control the size of custom pointers within the game. The size value ranges from 0 to 1, defaulting to 0.5.
The Layers plugin allows users to hide distracting layers within a game. This is a ranged value from 0 to 1. 0 indicates "show all layers" whereas 1 indicates "hide all distracting layers". By default, this value is 0.
Note that each game may implement this differently.
Note that these plugins accept HTML range inputs, rather than buttons.
import { ButtonSizePlugin, PointerSizePlugin, LayersPlugin, Container } from 'springroll-container';
const container = new Container('#game', {
plugins: [
new ButtonSizePlugin('#button-slider-selector', {
defaultButtonSize: 0.5, // button size goes from 0.0 to 1.0. Default = 0.5
}),
new PointerSizePlugin('#pointer-slider-selector', {
defaultPointerSize: 0.5, //pointer size goes from 0.0 to 1.0. Default = 0.5
}),
new LayersPlugin('#layers-slider-selector', {
defaultValue = 0 // goes from 0.0 to 1.0
})
]
});
container.openPath('game.html');
The HUD plugin allows users to position HUD elements within a game by docking to different sides of the screen.
import { HUDPlugin, Container } from 'springroll-container';
const container = new Container('#game', {
plugins: [
// expects exactly four(4) radio buttons with values "top", "bottom", "left", "right,
new HUDPlugin('#hud-position-button-selector', {
defaultValue: "top" //the initial starting value, defaults to "top"
}),
]
});
container.openPath('game.html');
The HUD Plugin requests the supported positions directly from the game itself and builds out an internal list of positions dynamically,
e.g. if the game supports Top and Bottom HUD docking (stored internally as ['top', 'bottom']
) then the plugin will hide the "left" and "right"
radio buttons so only the valid ones are displayed to users.
See the SpringRoll Application Class docs for more information on the request format and how game developers provide those values.
There are two plugins associated with in-game controls: ControlSensitivityPlugin
and KeyboardMapPlugin
.
The Control Sensitivity Plugin allows the user to determine the sensitivity of custom pointers in game. This plugin expects an HTML Input
The Keyboard Map Plugin allows users to re-map the keys/controls used in-game to something they are more comfortable with.
import { ControlSensitivityPlugin, KeyboardMapPlugin, Container } from 'springroll-container';
const container = new Container('#game', {
plugins: [
//ControlSensitivityPlugin expects an input of type=range for it's input.
new ControlSensitivityPlugin('#sensitivity-slider-selector', {
defaultSensitivity: 0.5, //control sensitivity goes from 0.0 to 1.0. Default = 0.5
}),
//The KeyboardMapPlugin expects a div or similar container element as it's selector. It will automatically build out the
//buttons to use for re-mapping controls based on what the application returns. See note below for HTML structure.
new KeyboardMapPlugin('#key-container', {
//you can provide a custom classname that will be attached to each key button that is generated
customClassName: 'custom-button-class' //default='springrollContainerKeyBinding__button'.
})
]
});
container.openPath('game.html');
*The Key Binding functionality of the KeyboardMapPlugin
works similarly to the HUDPlugin in that it requests information from the Springroll Application. See the SpringRoll Application Class docs for more information on the request format.
The HTML output within the key container will look like the following:
The className shown is the default, but can be overridden through the customClassName
option. The IDs are generated based on the action name.
<div id="keyContainer">
<label for="keyBoardMapPlugin-Up">Up</label>
<button class="springrollContainerKeyBinding__button" value="Up" id="keyBoardMapPlugin-Up">w</button>
<label for="keyBoardMapPlugin-Down">Down</label>
<button class="springrollContainerKeyBinding__button" value="Down"id="keyBoardMapPlugin-Down">s</button>
</div>
Keybindings are tracked visually by setting the textContent of the buttons themselves.
import { ColorVisionPlugin, Container } from 'springroll-container';
const container = new Container('#game', {
plugins: [
// expects exactly five(5) radio buttons with values "none", "achromatopsia", "tritanopia", "deuteranopia",
// and "protanopia" indicating the types of color visions supported.
new ColorVisionPlugin('input[name=color-vision-radios]'{
defaultValue: 'none' // initial checked radio box, defaults to "none"
}),
]
});
container.openPath('game.html');
*The color vision radio group builds out the supported values dynamically based on what the application reports back and hides any unsupported values. See the SpringRoll Application Class docs for more information on the request format.
The fullscreen plugin hooks up an element or elements to set the iframe to full screen then communicates this through Bellhop. The plugin will also add the class '--fullScreen'
to the element(s) given
import { FullScreenPlugin, Container } from 'springroll-container';
const container = new Container('#game', {
plugins: [
// FullScreenPlugin expects the selector for the element(s) to hook onto
new FullScreenPlugin('#fullScreenButton'),
]
});
container.openPath('game.html');
The plugin will accept either a selector or an array of selectors as a parameter
new FullScreenPlugin('#fullScreenButton');
new FullScreenPlugin(['#fullScreenButton', '.fullScreenButtonSideNav']);
// It will also accept one string with all selectors each separated by a comma
new FullScreenPlugin('#fullScreenButton, .fullScreenButtonSideNav');
The typical html can look something like this however, the element may be positioned anywhere in the html as long as it is not inside the iframe
<nav>
<!-- May be a button or any other element that can create an onclick event -->
<button id='fullScreenButton'>Fullscreen</button>
</nav>
<!-- The element cannot be inside the source file -->
<iframe id="game" scrolling="no"></iframe>
isFullScreen
returns true if there is a fullscreen element FullScreenPlugin.isFullScreen
All Plugins accept one or more HTML elements as controls in their constructor. For example the SoundPlugin can accept more than one volume slider or button if your set up requires it:
new SoundPlugin({
soundButtons: '#soundButton, #soundButtonTwo',
soundSliders: '#soundSlider',
musicButtons: '#musicButton',
musicSliders: '#musicSlider, #musicSliderTwo',
});
As long as the string you pass to the constructor is a valid selector string the plugin will use anything you pass to it. The plugins will keep settings in sync across the controls if necessary as well. Sliders will update each other, buttons will set a dataSet attribute or class (see individual plugin sections for the exact attribute), and any other controls will match each other appropriately.
*Note: at this time there is no support for multiple HTMLElements as parameters. If you are passing an HTMLElement as the parameter rather than a selector string you cannot pass multiple controls. If you do wish to use multiple controls, pass the plugin a selector string instead.
The openPath
method of the Container provides a mechanism for providing options directly to the game, called
playOptions
:
var container = new Container('#game');
container.openPath('game.html', {
playOptions: {
difficulty: 7,
theme: 'blue'
}
});
The Container adds any data included this way as queryString parameters that can be read by the SpringRoll Application.
Once the application finishes its init
process, this data will be available directly on the application instance:
var app = new springroll.Application({
// various options here
});
app.on('init', function() {
springroll.Debug.log('Play Options are', app.playOptions); // { difficulty: 7, theme: 'blue' }
});
Any JSON-serializable object can be set as a playOption
.
Container has an optional parameter at instantiation time which allows the opener to attach any data they require to the Container instance which any plugins can interact with to share data. This data can be mutated or replaced at any time.
const initialContext = {
hostEnvironment: 'web',
gameBuildInfo: {
commit: '#abcd123',
name: 'game-name'
}
};
const container = new Container('#game', { context: initialContext });
// Once the container instance is created you can access the context from any plugin or anywhere else there is access to Container
console.log(container.context); // { "hostEnvironment": "web", "gameBuildInfo": { "commit": "#abcd123", "name": "game-name" } }
The context can be accessed via the container.context
getter. It can also be modified by simply adding or changing fields
container.context.newField = 'hello';
container.context.gamebBuildInfo = 'newInfo';
// { "hostEnvironment": "web", "gameBuildInfo": "newInfo", "newField": "hello" }
console.log(container.context);
You can also overwrite the context object entirely if you need to:
container.context = {
newField: {
newKey: 'newField'
}
}
The only caveat here is that this will check to make sure the new data is an object. If not it will log an error and leave the context unchanged.
Copyright (c) 2024 SpringRoll
Released under the MIT License.