Skip to content

Tips & Tricks for Building Compositions with Seriously.js

brianchirls edited this page Jul 27, 2012 · 4 revisions

This article offers guidelines on how to best take advantage of the underlying architecture of Seriously.js. If you haven't already, it is recommended that you start with the Tutorial for an understanding of how Seriously works.

Detect compatibility and fail gracefully

Unfortunately, WebGL is not yet available everywhere, so some users aren't going to be able to view content created with Seriously.js. (See the "Requirements" section of the Seriously.js README for more information.) To provide the best experience for all your users, it's a good idea to make sure your site fails gracefully when the technical requirements are not met. Rather than allowing your script to break, hide any broken content and display a helpful support message offering guidance on how to address the problem. Many demos and tutorials will skip this step for the sake of brevity, but it's important for any complete, publicly available site.

In most cases, there are a few reasons why Seriously.js might not work:

  1. Your site is being viewed in an old browser that doesn't support the <canvas> element. This applies only to very old browsers, such as Internet Explorer before version 9.

  2. The browser doesn't support WebGL. This applies to Internet Explorer and most mobile browsers.

  3. The browser supports WebGL, but it is run on a system with old graphics hardware or old video drivers that don't sufficiently support the underlying OpenGL technology.

Seriously.js provides the incompatible method to help you determine whether the environment supports it and, if not, which support message to use. It can be used statically, as in the following simple example:

if (Seriously.incompatible()) {
  console.log('Seriously is not supported');
  //hide content, display alternate content or error message
}

If Seriously is supported, .incompatible() returns false. Otherwise, it returns a string that indicates the probable cause of failure.

var msg = '', status = Seriously.incompatible();
if (status) {
  if (status === 'canvas') {
    msg = 'Your browser does not support HTML Canvas. Please consider upgrading.';
  } else if (status === 'webgl') {
    msg = 'Your browser does not support WebGL. Please try Firefox or Chrome.';
  } if (status === 'context') {
    msg = 'Your graphics hardware does not support WebGL. You may need to upgrade your drivers.';
  } else {
    msg = 'Unable to display content.'; //unknown error
  }
  //display error message, hide main content and display alternate content
} else {
  //create Seriously instance, set up nodes and .go()
}

In certain cases, you may make use of plugin effects that requires advanced hardware features beyond basic WebGL (e.g. WebGL extensions or vertex textures). Seriously makes it easy to detect those features by passing the name of the plugin to incompatible as in this example:

if (Seriously.incompatible('fancy-effect')) {
  /* we can't use this effect, so remove it, replace it with something else,
     or use fallback content */
}

Finally, if we want to be very thorough, we can use incompatible as an instance method to verify that your entire composition will run. Used in this way, Seriously will verify first that the browser supports WebGL and then that it supports any effects included in your composition. An incompatible plugin will result in a string starting with 'plugin-' and then the name of the first plugin that failed.

var comp, effect;
comp = new Seriously();
console.log(comp.incompatible()); // false. everything is fine.

effect = comp.effect('fancy-effect');
console.log(comp.incompatible()); // returns 'plugin-fancy-effect' if unsupported

Save memory by destroying unused nodes

Every node (sources, targets and effects) in a Seriously composition caches a full copy of its processed frame. Seriously uses this cache to only re-render parts of the composition that change from frame to frame, resulting in very high frame rates. However, a frame can take up quite a lot of memory. Even though an image or video file may be very small due to compression, that image needs to be stored uncompressed in memory in order for the browser to display it.

For every pixel of every frame of video, we store 4 bytes - one each for the red, green, blue and alpha channels. For example, imagine a video that is not quite high definition, 960 pixels wide and 540 pixels high:

960 * 540 * 4 = 2,073,600

That's about 2 megabytes! How about full 720p HD?

1280 * 720 * 4 = 3,686,400 // ~3.5MB!

Looking at it this way, it's easy to see why web browsers take up so much memory. Most modern systems with decent enough GPUs to handle WebGL have enough memory that we can create quite a few nodes, but we want to be careful not to waste nodes, especially on a system that might be running many other tasks or have a small footprint, like a phone or Raspberry Pi.

Seriously.js keeps track of every node you create, so we can't count on the Javascript Garbage Collector to clean up wasted memory for us. But it does provide a destroy method that will completely delete and clean up any node we want.

//set up
var comp, source, target, effect;
comp = new Seriously();
source = comp.source('#video');
target = comp.target('#canvas');
effect = comp.effect('invert');
target.source = effect;
effect.source = source;
comp.go(); //start rendering

//okay, now we're done with the effect
target.source = source;
effect.destroy();

effect.source = source; //does nothing
effect.render(); //does nothing

//finally, set effect to null so the object gets completely garbage collected
effect = null;

We can also destroy an entire composition. This does a full clean-up of the composition and any nodes created for it. var comp, effect; comp = new Seriously(); effect = comp.effect('invert');

comp.destroy();

//now, both comp and effect have been destroyed, and the main frame cache has been released.

Once a composition or a node is destroyed, it cannot be used again, so if you want it back, you have to re-create it from scratch.

Finally, if we need to check whether a node or composition has been destroyed, we can use the isDestroyed method. var comp = new Seriously(); effect = comp.effect('invert'); console.log(comp.isDestroyed()); //false console.log(effect.isDestroyed()); //false comp.destroy(); console.log(comp.isDestroyed()); //true console.log(effect.isDestroyed()); //true