Coder Social home page Coder Social logo

bergson's Introduction

Bergson

What is Bergson?

Bergson is the scheduling system for Flocking and Aconite. It provides a variety of clocks driven by different sources (such as requestAnimationFrame, the Web Audio API, and setInterval), and a priority queue-based scheduler that allows you to queue up one-time and repeating function calls.

Bergson provides a very low-level API for scheduling, and is intended for library developers to build their own rhythmic and pattern-based abstractions on top of.

Examples

Creating a Scheduler with the requestAnimationFrame Clock

var scheduler = berg.scheduler({
    components: {
        clock: {
            type: "berg.clock.requestAnimationFrame",
            options: {
                freq: 60
            }
        }
    }
});

scheduler.start();

Scheduling one-time events

scheduler.schedule({
    type: "once",
    time: 30            // 30 seconds from now
    callback: function (now) {
        // Do something in the future.
    }
});

Scheduling repeating events

scheduler.schedule({
    type: "repeat",
    time: 5,            // Start repeating five seconds from now.
    freq: 2,            // Repeat every two seconds.
    end: 20,            // Stop repeating 20 seconds from now.
    callback: function (now) {
        // Do something repeatedly.
    }
});

Listening for Clock Events Directly

scheduler.clock.events.onTick.addListener(function (time, rate) {
    // Do something every time the clock ticks.
});

Bergson API

Scheduler API

Score Event Specifications

A score event specification is a JSON-type (i.e. plain old, non-prototypal) object that describes the event to be scheduled. It can contain the following properties.

Property Value Description
type "once" or "repeat" The type of event to schedule (either one-time or repeating).
time Number The future time in seconds when the scheduled event should occur.
interval Number (Only for repeating events) The time interval, in seconds, between repetitions. This option takes precendence over freq if both are provided.
freq Number (Only for repeating events) The frequency at which the event should repeat. This option will be overridden by interval if both are provided.
end Number (Only for repeating events) The future time in seconds when the scheduled event should stop repeating.
callback Function The function to invoke.

Scheduler Model

For more information on interacting with models, see the Infusion documentation about the Change Applier.

Property Description Types
timeScale The scheduler's time scale factor. Defaults to 1.0. Number

Scheduler Members

Property Description Types
queue The priority queue used to sort and queue score events. berg.priorityQueue

Scheduler Methods

Method Description Argument Types Returns
start Starts the scheduler and its clock. none undefined
stop Stops the scheduler and its clock. none undefined
schedule Schedules an event A _score event specification_ object The scheduled score event specification.
clear Removes a scheduled event from the scheduler queue. The _score event specification_ to clear. undefined
clearAll Removes all scheduled events from the scheduler queue. none undefined
setTimeScale Sets the time scale factor for the scheduler. This will cause all time values for current and future scheduled events to be scaled by the specified value. Number undefined

Clock API

Clock Members

Property Description Types
time The current clock time, in seconds. Number
freq The clock's frequency, in cyles per second. Number
tickDuration The duration between ticks, in seconds. Number

Clock Methods

Method Description Argument Types Returns
start Starts the clock. none undefined
stop Stops clock. none undefined
tick Advances the clock's time. (This will be invoked automatically for most clocks.) None undefined

Clock Events

Event Name Description Argument Types
onTick Fires each time the clock ticks. Number time: the current clock time in seconds

Types of Clocks

Clock Description Default rate
berg.clock.requestAnimationFrame A clock that is driven at (or as near as possible to) the display's refresh rate by the browser's built-in requestAnimationFrame function. Note that this clock will be throttled down to 1 Hz automatically by the browser if its tab is not active. 60 Hz. You must override this value if your display is running a different refresh rate.
berg.clock.audioContext Uses the Web Audio API's AudioContext as a source of time. This clock needs to be driven by your own ScriptProcessorNode code (i.e. by calling its tick() method within your onaudioprocess handler). This clock will not start ticking until its start() method is invoked by a user action (such as a button click). Dynamically determined based on the AudioContext's sampleRate and this clock's blockSize option.
berg.clock.autoAudioContext An AudioContext-based clock that will automatically create a ScriptProcessorNode instance and wire it up correctly for you. Use this if you're not using the Web Audio API directly, but want a rock-solid clock for scheduling events in your application. This clock will not start ticking until its start() method is invoked by a user action (such as a button click). Dynamically determined based on the AudioContext's sampleRate and this clock's blockSize option.
berg.clock.setInterval A clock that is driven by the browser's built-in (and notoriously jittery) setInterval function. Use this if you don't need significant reliability, and want maximal cross-environment compatibility (e.g. with Node.js). Note that this clock will be throttled down automatically by the browser if its tab is not active. 10 Hz
berg.clock.workerSetInterval Like berg.clock.setInterval, except that it employs a Web Worker in order to avoid the standard throttling done by browsers when the page is in the background. 10 Hz
berg.clock.offline A base grade for creating your own types of clocks. An offline clock tracks time relatively (i.e. without reference to a "real" source of time such as the system clock). This clock should be driven manually by invoking its tick() method. 1 Hz
berg.clock.realtime A base grade for creating your own types of clocks. A realtime clock tracks time based on the actual system time using, by default, performance.now(). This clock should be driven manually by invoking its tick() method. 1 Hz

Building and Testing Bergson

Prerequisites

Bergson uses Node.js, npm, Grunt, and Testem for its build and test tooling. Here's how to get set up:

1. Install the [Node.js](https://nodejs.org/en/) LTS release
3. Install Grunt: <code>npm install -g grunt-cli</code>
4. Install Testem: <code>npm install -g testem</code>

How to Build Bergson

To download all of Bergson's dependencies, build them, and then build Bergson itself, run the following commands:

npm install
grunt

Running Bergson's Test Suite

Bergson's test suite can be run on both Node.js and all of the browsers installed on your computer using Testem:

npm test

If you'd like to run only the Node.js tests, run:

npm run node-test

Or if you only want to run the browser tests:

npm run browser-test

Alternatively, if you'd like to only run the tests in one browser, you can open the test suite tests/unit/all-tests.html file by hand in your browser.

Community

Bergson is supported by the Flocking community and uses its forums:

Mailing List

The Flocking mailing list is the place to ask questions, share code, and request new features.

Chat

Flocking has an IRC channel, which can also be used to ask questions about Bergson. Join #flocking on irc.freenode.net.

Credits and License

Bergson is written and maintained by Colin Clark. It is dually licensed under the MIT and GPL 2.0 licenses.

bergson's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

bergson's Issues

The scheduler should not mutate user data

Currently, scoreEventSpec objects provided by users of the API are internally modified by the Scheduler. Instead, we should record IDs for each score event and create our own internal record that can be expanded and modified as needed without modifying a user's data directly.

The downside of this approach, of course, is that more objects get created per event, but if this becomes an issue a pool of internal records could be used.

Add support for running builds and tests in CI

Currently there's no CI system for Bergson. It would be nice to run its unit tests and the Grunt build automatically when PRs are submitted. I'm sure Infusion has an example for how this can be done with (hopefully) Headless Chrome and Firefox?

Bergson's dist directory is ignored when it is npm installed

Despite producing the dist directory as the result of a prepublish script, npm is ignoring the directory because it doesn't include an .npmignore file and as a result it's being inherited from .gitignore.

An empty .npmignore file is needed to ensure that the directory isn't elided when installing Bergson.

Add support for modulatable clock frequencies

Currently in Bergson, the frequency of a clock is static. It is specified as an option at construction time, and cannot be modified later without a lot of obscure effort.

We should try to reduce caching wherever it won't impact performance so that the frequency can be modulated freely, and then expose freq as a model property that writes to this member so that Bergson clocks and schedulers can interoperate easily with other Infusion model components.

From a quick look through Bergson's source code, it looks like a clock's tickDuration member is calculated and cached at creation time from its freq, and in turn a scheduler's lookahead member is calculated at construction time from the clock's tickDuration. These are quite trivial calculations that should be inlined by the JIT, so can probably be moved into the tick loop to allow for modulation.

Provide a means for determining if a Bergson clock is currently ticking

At the moment, Bergson clocks do not provide any kind of state the convey whether or not they're currently running. Despite providing start() and stop() methods, berg.clock does not keep track of this via any kind of model (or otherwise) state.

Given that changes to the playback state will be fairly infrequent, it makes sense to express this state as a boolean model value (so that it plays nice with modelized play/pause button UI components, etc.)

Update to latest third party dependencies

It's been a long time since third party dependencies have been updated.

Notably, Bergson hasn't been well tested with Infusion 4.x, and the tests fail due to newer versions of Infusion having immutable options(a good thing), while Bergson's scheduler tests try to directly modify its options (a bad thing).

A berg.workerSetInterval clock can't be stopped and then restarted

Issuing the following sequence will silently fail when using a berg.workerSetInterval:

var clock = berg.workerSetInterval();
clock.start();
clock.stop();
clock.start(); // Will never restart.

It looks like the clock's current logic is to terminate its backing Worker object whenever it is stopped, however, the worker only ever created when the clock is first initialized and never again.

Either we should treat the backing worker for this clock as transient and create it whenever the clock is started, or it should be persistent and only terminated when the component is destroyed. The latter seems preferable.

Remove all built files from the repository

This can be accomplished in two ways:

  1. Removing Bower support entirely from Bergson and building it at NPM's prepublish hook
  2. Retaining Bower support and running grunt at Bower's postinstall hook

Since Infusion doesn't support Bower, I'm tempted to take the first approach and simply require npm.

Allow users to schedule a repeating event by interval, rather than frequency

Currently, repeating events must be scheduled by specifying a freq option, which is the number of repetitions that should occur per second. However, in some cases it's easier to specify the interval of time between repeating events instead. Internally, the Bergson schedule immediately converts the freq option to an interval; it should be possible for users to choose which form they prefer to use.

It's possible to observe time leaping forward when using a berg.clock.realtime

A Bergson realtime clock initializes its time member to the current now time when it is constructed. However, by default, clocks don't start ticking (and thus regularly updating their time until the start() method has been called on them.

As a result, when start() is called, it will appear to clock observers as if time suddenly leaps forward from the creation time to the current time. This means that any events that were "optimistically" scheduled while the clock was not ticking, with the intention of their timers starting when the clock starts ticking, will actually be scheduled to occur right away.

This may be what users want, or it may not. It is probably advisable for users to either:

  • ensure that the clock is running constantly, or
  • avoid scheduling any events until the clock has started and the next onTick event fires.

Or, alternatively, perhaps a berg.scheduler instance should simply refuse to schedule any new events while its clock isn't ticking?

If not the latter, we should improve the documentation to ensure that this issue is noted and provide solutions for avoiding it.

It's difficult to use a berg.scheduler.workerProxy because of file path issues

Currently, the berg.scheduler.workerProxy requires a separate JavaScript file to be loaded into the worker (which includes Infusion and everything else needed by Bergson). When consuming Bergson as an npm module and using its dist/bergson-only.js file, the worker's JavaScript file is not found, and a user has to override this value (which is difficult in the case where the client of Bergson is itself a framework).

Upgrade to Infusion 2.x

Fluid Infusion has been massively overhauled for the 2.x line, and the basis of this has now been merged into Infusion's master branch. The new version includes performance improvement and a simpler and more powerful API.

We should upgrade Bergson to match the new API.

Bergson's scoreEventSpec format should include optional namespaces

Currently, there's no way to clear a specific collection of score events from the Bergson scheduler; you can either clear a single event (via reference), or clear all events. It would be nice for a client to be able to clear only the events it scheduled (by specifying a namespace for those events), rather than clobbering them all in cases where there is a shared global scheduler.

Code review

Bergson is at least functionally complete in its "minimum viable" form, and ready to be used in Aconite and Flocking.

But the source code is still a bit snarly in some spots, and could benefit from a comprehensive code review prior to releasing version 1.0.0.

@amb26, are you willing to look over the code and offer suggestions? I'm particularly interested in feedback related to factoring, idiomatic use of Infusion, and performance considerations (since Bergson will be run at frame rate in Aconite and once per block, or 700+ times per second, in Flocking).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.