The latest version, SpringRoll 2, is a large departure from its predecessor. SpringRoll 2 is less prescriptive and contains only a few guidelines and restrictions. Instead, the framework focuses on utilities to help developers make games more accessible and deployable at scale.
- Adding Sound (WebAudio)
- Hardware Rendered Games (via WebGL)
- Implementing Captions
- Remote Debugging
- Responsive Interfaces
- Game State Management
- Preloading Assets
- Browser Cache Control
The primary objective of the framework is to build content which supports WebAudio API and WebGL (with Context2d fallback). We have intentionally avoided support for these browsers:
- Android stock browser (no WebAudio support or suitable fallback support)
- Internet Explorer 7/8 (no HTML5 canvas support)
- ColorFilter
- SpeechSynth
- Application
- Controller
- Key
- Debugger
- Caption
- CaptionFactory
- CaptionPlayer
- CaptionPlayer
- milliToSec
- TimedLine
- Localizer.Options
- Localizer
- ApplicationPlugin
- ScaleManager
- Property
- StateManager
The Color filter allows you to simulate colorblindness.
It has built in support for:
Protanopia
Protanomaly
Deuteranopia
Deuteranomaly
Tritanopia
Tritanomaly
Achromatopsia
Achromatomaly
To use it, all you have to do is pass it a element and your desired filter.
import { ColorFilter } from './ColorFilter';
const colorFilter = new ColorFilter();
colorFilter.applyFilter(document.getElementById('your-id'), 'protanopia');
You can change the filter at any time. using changeFilter();
colorFilter.changeFilter('deuteranopia');
Applies the specified filter to the element.
element
HTMLElementtype
string
Changes the currently applied filter to the element if the color filter has it.
type
string
Returns any
Removes the filter from the element.
Supported filter types.
Returns object Returns an object { name, value } with the colorblindness type: (Protanopia, Protanomaly, Deuteranopia, Deuteranomaly, Tritanopia, Tritanomaly, Achromatopsia, Achromatomaly)
SpeechSync makes it easy to convert text to speech.
Using it is as easy as constructing and passing it a string.
import { SpeechSynth } from '...';
const speaker = new SpeechSynth();
speaker.say('Hello world!');
- Any additional strings passed to it while it's still playing will be queued and will automatically start playing after finishing the current string, unless canceled.
You can also control what the starting params of the voice are by passing in a params object on construction.
const speaker = new SpeechSynth({voice:0, rate:1, pitch:0, volume:1});
You can also change it any time by changing the properties on the object.
speaker.rate = 10;
speaker.pitch = 2;
speaker.volume = 0.5;
speaker.voice = 30; //Note this one is browser specific and won't work in all cases
- If you would like to know what voice options are available for your browser, the class instance contains a reference to all options.
speaker.voiceOptions // [Object]
params
objectparams.voice
number Indicates what voice to use. (optional, default0
)params.rate
number The rate at which the text is said. Supports a range from 0.1 to 10. (optional, default1
)params.pitch
number Voice Pitch. Supports a pitch from 0 to 2. (optional, default0
)params.volume
number Volume. Supports 0 to 1. (optional, default1
)
voicesLoaded
boolean voices are loaded async. This is will be set to true when they are loaded.
Pauses the announcer.
Resumes the announcer.
Pauses the announcer and clears the queue.
Causes the announcer to say whatever message is passed to it. If the announcer is already saying something then it will be added to a queue.
message
string
Sets the voice by array index.
index
number
Returns the voice object.
Returns (object | null)
Rate at which text is spoken.
rate
number
Returns rate which text is spoken.
Returns number
Sets the pitch at which text is spoken.
pitch
number
Returns the pitch at which text is spoken.
Returns number
Sets the current volume of the announcer.
volume
number
Returns the current volume of the announcer.
Returns number
The SpringRoll Application provides the main entrypoint for games. In particular, it provides access to any functionality provided by plugins along with access to any state set or changed by the container.
var application = new springroll.Application();
application.state.ready.subscribe(function(isReady) {
if(!isReady) {
return;
}
// all plugins have loaded
});
When an Application is embedded via a SpringRollContainer it can notify the container of it's supported features. To do this in a SpringRoll Application, pass them as an object to the constructor:
var myApp = new springroll.Application({
captions: false, // whether or not the game has captions
sound: false, // whether or not the game has any sound
vo: false, // whether or not the game has a VO
music: false, // whether or not the game has music
sfx: false, // whether or not the game has any sound effects
});
Note that if any of vo
, music
, or sfx
are available features, sound
will be marked as a feature implicitly.
Also, all of these features are marked false
by default.
When certain features are enabled, SpringRoll warns if an associated state change listener is missing. For instance,
if sound
is enabled as a feature of the game, there must be a subscriber to the soundMuted
state:
var myApp = new springroll.Application({
sound: true
});
myApp.state.sound.subscribe(result => console.log('Is sound muted?', result));
For each possible feature, there is an associated state that can be subscribed to:
var myApp = new springroll.Application({
captions: true,
sound: true,
vo: true,
music: true,
sfx: true
});
myApp.state.captionsMuted.subscribe(result => console.log('Are captions muted?', result));
myApp.state.sound.subscribe(result => console.log('Is sound muted?', result));
myApp.state.vo.subscribe(result => console.log('Is VO muted?', result));
myApp.state.music.subscribe(result => console.log('Is music muted?', result));
myApp.state.sfx.subscribe(result => console.log('Is SFX muted?', result));
Lastly, there are two other states available, one that has already been mentioned:
var myApp = new Application();
myApp.state.ready.subscribe(() => {
console.log('The app is ready. All plugins have finished their setup and preload calls');
});
myApp.state.pause.subscribe(isPaused => {
console.log('Is the game paused?', isPaused);
});
Converts a callback-based or synchronous function into a promise. This method is used for massaging plugin preload methods before they are executed.
callback
Function A function that takes either a callback, or returns a promise.
Returns any Promise A promise that resolves when the function finishes executing (whether it is asynchronous or not).
Validates that appropriate listeners are added for the features that were enabled in the constructor
- Throws any Error
The list of plugins that are currently registered to run on Applications.
Registers a plugin to be used by applications, sorting it by priority order.
plugin
ApplicationPlugin The plugin to register.
Controller interface class to simplify working with key presses.
It accepts a array of objects that follow this format Object {key: string, down?: function, up?: function}
import { Controller } from ...
const controller = new Controller([
{ key: 'ArrowLeft', down: () => console.log('left arrow pushed down!')},
{ key: 'ArrowDown', down: () => console.log('down arrow pushed down!')},
{ key: 'ArrowRight', down: () => console.log('right arrow pushed down!')},
{ key: 'ArrowUp', down: () => console.log('up arrow pushed down!')},
{ key: 'Enter', down: () => console.log('enter pushed down!')},
{ key: ' ', down: () => console.log('space bar pushed down!'), up: () => console.log('space bar stopped being pushed down!')},
]);
-
Key values can be found here
-
Key values are case insensitive.
The controller will monitor key inputs for you. Call the function when you wish to call the functions that are related to the currently active keys.
controller.update();
If at any time you wish to change the keys currently being watched, you can call the assignButtons function. This follows the same format as the constructor:
controller.assignButtons([
{ key: 'a', down: () => console.log('a pushed down!')},
{ key: 's', down: () => console.log('s pushed down!')},
{ key: 'd', down: () => console.log('d pushed down!')},
{ key: 'w', down: () => console.log('w arrow pushed down!')},
{ key: 'Enter', down: () => console.log('enter pushed down!')},
{ key: ' ', down: () => console.log('space bar pushed down!'), up: () => console.log('space bar stopped being pushed down!')},
]);
buttons
Array An object containing all keys you want to watch and their functions. e.g. {enter: () => {}}. See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values for potential values. (optional, default[]
)
Calls all functions for buttons that are currently set to enabled.
Called on keyup. Sets flag to 1 if key is being watched.
Called on keyup. Sets flag to 2 if key is being watched.
Sets an object of button functions to the controller to be called.
buttons
Array
Represents a single key on the keyboard and the functions related to it.
key
string What this object represents.down
function Function to be called while the key is held down. (optional, default()=>{}
)up
function Function to be called when the key is lifted up. (optional, default()=>{}
)
state
(0
|1
|2
) The current state of the key. 0 = inactive, 1 = active, 2 = to be set to inactive.key
string The name of the key we are targeting.actions
object
Updates the internal state of the key. Accepts a range between 0-2. Will set key state to 0 if nothing is passed.
state
(0
|1
|2
) (optional, default0
)
Calls the relevant action for the current state of the key.
Returns the current state of the key.
Returns number
Type: object
Simplifies logging events to the console for debugging purposes.
params
Object Optionsparams.emitEnabled
boolean If this should emit events to the window. (optional, defaultfalse
)params.minLevel
("GENERAL"
|"DEBUG"
|"INFO"
|"WARN"
|"ERROR"
) The starting log level for the logger. (optional, default'GENERAL'
)
Returns the params of the debugger.
Returns DebuggerParams
Sets the logging level of the debugger.
Returns void
If emitting is enabled for this instance, then it will dispatch an event on the window.
eventName
string Name of the event (optional, default'Debugger'
)
Returns logging levels.
Returns object
Console logs all supplied arguments if the log level is low enough for them to be logged.
type
("log"
|"general"
|"warn"
|"error"
|"debug"
|"info"
) minimum level for this log to run at (optional, default'log'
)args
Array<any> Arguments you wish to log.
Will throw if statement is false.
isTrue
boolean The expression to evaluate.
Returns a boolean indicating if the debugger has been enabled or not.
Returns boolean
Disables or enables all debugger instances.
flag
boolean
Returns void
Returns the global params key.
Updates content based on time passed.
This should be called every frame that the caption is active.
deltaTime
Number Time in seconds since last frame.
Handles calling callbacks and updating caption's current state.
Checks if caption has completed.
Returns Boolean
Sets time and line index of caption.
time
Number Time in milliseconds. (optional, default0
)beginCallback
(optional, default()=>{}
)endCallback
(optional, default()=>{}
)
Collection of functions for creating
The CaptionPlayer object provides a simplified way to handle playing captions in your game.
In order to play a caption you'll first need to initialize a captions player and an object for rendering your captions.
import { CaptionPlayer, CaptionFactory } from 'springroll/localization'
// Start and end times are in Milliseconds
const captionData = {
"welcome":[
{
"content": "This is the first line"
"start":0,
"end": 1200
},
{
"content": "This is the second line"
"start":1300,
"end": 2400
}
],
"other":[
{
"content": "this caption only has on line"
"start":0,
"end": 3000
}
]
}
const captionsElement = document.getElementById("captions");
const captionMap = CaptionFactory.createCaptionMap(captionData);
const captionPlayer = new CaptionPlayer(captionMap, {
start:() => {
captionsElement.style.visibility = "visible";
},
lineBegin:(line) => {
captionsElement.innerHTML = line.content;
},
lineEnd:() => {
captionsElement.innerHTML = '';
},
stop:() => {
captionsElement.style.visibility = "hidden";
}
});
Each line in a caption must have a start and end time, if you want to have a delay between lines you should add time to the start of the next line. It's not recommended to use a line with an empty content.
If line B
's start time is before line A
's end time, then A
will finish before B
starts.
A caption renderer can have the following callbacks.
Name | Time |
---|---|
start() |
Called when CaptionPlayer.start() is called |
stop() |
Called when CaptionPlayer.stop() is called or when caption is finished |
lineBegin(line) |
Called at the beginning of each line after CaptionPlayer.start() |
lineEnd() |
Called at the end of each line, called before CaptionPlayer.stop() |
The caption player needs to be updated regularly in order for it to function properly. It's recommended to call update on every frame for the most accurate timing.
// DeltaTime is the time passed in SECONDS since the last update call.
captionPlayer.update(deltaTime);
To start playing a caption, you call start. You can pass a start time in as an optional parameter.
captionPlayer.start('welcome');
captionPlayer.start('welcome', 1200);
Note: the CaptionPlayer can only play one caption at a time
Captions automatically stop when the time passed is greater than the end time. You can manually stop them if you need to.
captionPlayer.stop();
class HTMLCaptionRenderer
{
constructor(element)
{
this.element = element;
}
start() => {
element.style.visibility = "visible";
}
lineBegin(line) => {
element.innerHTML = line.content;
}
lineEnd() => {
element.innerHTML = '';
}
stop() => {
element.style.visibility = "hidden";
}
}
class YourGame
{
preload()
{
this.loader.load('assets/captions.json', 'captionData');
}
start()
{
const captionsElement = document.getElementById("captions");
const captionMap = CaptionFactory.createCaptionMap(this.cache.getJSON('captionData'));
this.captionPlayer = new CaptionPlayer(captionMap, new HTMLCaptionRenderer(captionsElement));
this.captionPlayer.start('example');
}
update()
{
this.captionPlayer.update(this.Time.DeltaTime * this.Time.scale);
}
}
Creates a new Object<String, Caption>.
data
JSON
Returns Object
Creates a new Caption from JSON data.
captionData
JSON
Returns Caption new Caption
Creates a new TimedLine from JSON data.
lineData
JSON
Returns TimedLine new TimedLine;
Object used to render caption.
captions
renderer
Updates any currently playing caption.
This should be called every frame.
deltaTime
Number Time passed in seconds since last update call.
Starts playing a caption.
Returns boolean True if caption started.
Stops any caption currently playing.
CaptionPlayer is used to start, stop and update captions. It applies the content of an active caption to a given CaptionRenderer.
Updates any currently playing caption.
This should be called every frame.
deltaTime
Number Time passed in seconds since last update call.
Starts playing a caption.
Returns boolean True if caption started.
Stops any caption currently playing.
time
Sets line's content. Removes HTML formatting for text.
content
any
Returns void @memberof TimedLine
Type: {language: string, fallback: string}
The localizer object provides a layer above your file loader to help with loading localized files.
In order to use the localizer you'll have to provide a config with a default locale, as well as all the locales you wish to use.
import { Localizer } from 'springroll/localization';
const config = {
"default":"en",
"locales":
{
"en": { "path": "assets/en/" },
"fr": { "path": "assets/fr/" },
"fr-ca": { "path": "assets/fr-CA/" }
}
}
const localizer = new Localizer(config);
The localizer will automatically look for the browser's language and use it. A fallback locale will also be set from default in the config. The fallback is used automatically if a specified language can't be found in locales.
An options object can also be provided to manually set the target and fallback languages.
const localizer = new Localizer(config, { language:'fr', fallback:'en'});
localizer.resolve()
Returns an object that contains the resolved path.
let result = localizer.resolve('vo/welcome.mp3');
loader.load(result.path, 'welcome');
This will load a file relative to the current locale, for example if the browser language was set to French-Canadian, the path supplied to the load function would look like: assets/fr-CA/vo/welcome.mp3
.
You can also provide an options object to override the target and fallback languages for only a single load event.
let result = localizer.resolve('vo/welcome.mp3', { language: 'es-ES', fallback: 'en' });
loader.load(result.path, 'welcome');
If the language or fallback are not found in the locales, then it will load with the default fallback. For example: if 'es-ES'
is not found, the load function will try 'es'
if that isn't found, it will use the fallback language 'en'
.
Result also contains the language key of the language used.
let result = localizer.resolve('vo/welcome.mp3', { language: 'es-ES', fallback: 'en' })
console.log(result.language); // 'en'
class YourGame
{
init()
this.localizer = new Localizer(this.localizerConfig);
}
preload()
{
// load all your localized files.
let result = this.localizer.resolve('vo/welcome.mp3')
this.loader.load(result.path, 'welcome');
result = this.localizer.resolve('local.json')
this.loader.load(result.path, 'local');
//...
// Any non localized files don't have to go though the localizer.
this.loader.load('assets/images/Foo.png', 'fooSprite');
//...
}
start()
{
//Do things with loaded files;
let welcome = new Sound('welcome');
welcome.play();
//...
}
}
path
options
any (optional, default{}
)Path
string
Returns Promise
localeKey
string
Returns boolean True if language is set.
localeKey
string
Returns boolean True if fallback is set.
localeKey
string
Returns string
Returns Array<string> An array of browser languages.
Represents a single plugin for applications. Allows developers to inject code in the start up process of an application providing new features to the application.
import { ApplicationPlugin } from 'springroll/plugins/ApplicationPlugin';
export default class CustomPlugin extends ApplicationPlugin {
constructor() {
let priority = 20;
super(priority);
}
setup() {
// custom synchronous code. `this` is bound to the current Application
this.customContent = {};
}
preload() {
// custom asynchronous code. Expected to return a Promise.
return fetch(someApiEndpoint)
.then(response => response.json())
.then(json => this.customContent = json);
}
}
Once you've created a plugin, you'll need to register it before instantiating the application:
import { Application } from 'springroll';
import CustomPlugin from './CustomPlugin';
Application.uses(new CustomPlugin());
const myApp = new Application();
myApp.on('init', function() {
console.log('Ready!');
});
A setup method for the plugin. This method is ran synchronously in the constructor of the Application.
A preload method for the plugin which allows for asynchronous setup tasks. Either takes a callback as first parameter, or should return a Promise indicating that loading is finished.
Returns (Promise | undefined) If defined, treated as promise indicating when the plugin is finished loading.
Simplifies listening to resize events by passing the relevant data to a provided callback.
callback
Function (optional, defaultundefined
)
callback
Function?
Enables the scale manager listener. Will not be enabled if a callback is not supplied.
callback
Function The function to be called on resize events. (optional, defaultundefined
)
Disables the scale manager.
A class for representing changeable/subscribable properties.
Notifies all subscribers to the property of a new value.
Adds a subscriber to this property.
callback
Function
callback The callback to call whenever the property changes.
Unsubscribes a listener from this property.
callback
Function
callback The callback to unsubscribe.
Whether or not this property has any subscribed listeners
Returns any Boolean True if this property has at least one subscriber
A class for managing a group of subscribable properties together. Allows for the registration of new properties.
For example:
var manager = new StateManager();
manager.addField('paused', false);
manager.paused.subscribe(function(newValue) {
console.log('New value is ', newValue);
})
manager.paused = true;
Adds a new subscribable field field to the state manager. Throws an error if the field already exists.
name
String The name of the field.initialValue
Any The initial value of the property.
Returns any Property The newly created property.