Tech Note: Creating External Scenes by Hand

From Waltz
Jump to navigation Jump to search

This tutorial assumes an existing understanding of HTML, CSS, and JavaScript and how those technologies interact with each other. For a slightly more user-friendly way to create External Scenes, try Creating External Scenes with Hype.


Scenes in Waltz use standard web technologies, including HTML5, CSS, and JavaScript, to create interactive user experiences inside a standard web browser on both desktop and mobile devices. Waltz contains built-in tools to create scenes, but sometime a greater level of creative control is desired. In these situations an External Scene, built outside of Waltz, can be integrated into Waltz to trigger aspects of your show, as well as display real-time information from Waltz.

Required Dependancies

External Scenes require that your webpage include two script files, which are hosted by Waltz. The files may be updated or change between versions of Waltz, and should not be included by any other method than requesting them from Waltz.

Adding the following two lines to the <head></head> of your HTML document will include the dependancies required to communicate with Waltz.

<script src="/api/jquery.js"></script> 
<script src="/api/waltz.js"></script> 

Waltz requires jquery.js be present in order to function, and it must be included before the waltz.js. You may also provide an up-to-date version of jQuery yourself and omit this import from Waltz.

Initializing Waltz Support

With the two required files loaded, you will have minimal integration with Waltz. Where the integration shines is in the ability to add special classes and data tags to existing elements to perform actions, track values, and display materials from Waltz. For this functionality to work, you need to initialize the Waltz object in JavaScript. This should be done after the page loads and you have created your full DOM. Initializing Waltz support is done with a single call in JavaScript, shown here wrapped in jQuery to wait until the full page has loaded.

$(function() {
    Waltz.init(30);
});

The 30 in the example is the number of times per second that information will be updated. While it is normal to run Waltz at 60+ FPS, we generally recommend scenes run at about 30 FPS to reduce the amount of network traffic, particularly on wireless networks.

Performing Actions in Waltz

Actions are similar to Scripts in Waltz. They are able to interact with any node or nodes on the stage, and the output of the script does not matter. The script is execute every time an element is clicked/tapped or when any event as defined below has occurred.

Any element in the DOM that supports JavaScript events can execute an action on click/tap, or if desired on a specially assigned event for that element.

Waltz integration uses Classes to describe available functionality on an element. To perform an action, first add the waltzAction class to the element. The waltz.js library looks for this, as well as other special classes, to inform the configuration of an element. Multiple classes may be used on a single element. The classes defined by Waltz do not apply any style themselves, unless you configure them to do so via CSS. After adding the waltzAction class, you then add a custom data attribute named data-waltz-action to the element with the script you wish to be executed by Waltz as its value. This script is the same JavaScript you would use inside Waltz to perform an action, and in fact the full script you provide here will be executed inside Waltz, not on the local webpage. For example, a button can be configured to send an OSC message using a node named oscOutput from our show with the follow snippet of HTML.

<button class="waltzAction" data-waltz-action="$.oscOutput.send()">Do the cool thing!</button>

When you call Waltz.init(30), this will automatically be converted into a button that can communicate with Waltz for you, without having to define additional action handlers.

It is considered best practice to, rather than call functions on nodes directly from a webpage, use a Script Function node that optionally takes any number of parameters, and set actions on the webpage to run that script function instead. In the example above, let's assume we have a Script Function node named sendMyMessage which has a single parameter configured. To use our new script function we would replace the value in data-waltz-action with $.sendMyMessage.run(99) to accomplish this. This allows you to make edits to the exact way messages are handled without having to edit the webpage source later.

If you want, for example, the action to only be performed when the mouse stops hovering over the element, you can add a data-waltz-action-event attribute with a value of mouseleave to the button, and Waltz will attach the action to the specified event instead of the default (which varies depending on whether a touch-screen is present). The HTML for the same button would then look like:

<button class="waltzAction" data-waltz-action="$.oscOutput.send()" data-waltz-action-event="mouseleave">Do the cool thing!</button>

If additional actions that affect the local page are desired, you should also add standard callbacks to the element yourself.

Displaying Values from Waltz

Displaying values from Waltz is just as simple as performing actions, and works mostly in the same. The contents of the configured element will be emptied and replaced with the value from Waltz, so it is generally best to use a small element, like a span, to contain only the data from Waltz. To make an element contain a value from Waltz, it must have waltzValue added to its classes, and have a data-waltz-value attribute with an expression from which the value will come. In the example below, the value of a Wave node, conveniently called wave, is displayed with a title preceding it.

<p>My great wave number is: <span class="waltzValue" data-waltz-value="$.wave.output"></span></p>

In this example, notice how we apply the Waltz-specific properties only to the inner span element, not the full paragraph. If we applied the properties to the paragraph, our title before the number would not be visible.

Showing Materials from Waltz

The final supported integration with Waltz is the displaying of materials on the web page. On web pages, materials are backed by canvas elements, but Waltz will handle this implementation detail for you. In generally, you will use a div element with a class of waltzMaterial and a data-waltz-material attribute with an expression referring to a material in Waltz. For example, to show a 300x200 pixel graph from a node named graph, you would write…

<div style="width: 300; height: 200;" class="waltzMaterial" data-waltz-material="$.graph.material"></div>

Notice that in order to make the div the correct size for our specific material, we define the width and height. A material should be scaled before being broadcast over the network if a different size is needed.

Styling Elements from Values in Waltz

In addition to displaying values inside elements, Waltz also allows you to dynamically style the element based on a value from inside Waltz. This is accomplished with the waltzStyle class, similar to the usage of the waltzAction or waltzValue classes. After assigning the waltzStyle class to an element, you can add any number of data tags to the element, on for each CSS property you wish to effect. Each tag must have a unique name and must be in the format of:

data-waltz-style-{css-property-name}

For example, to change the background color of an element, you would add a data tag named:

data-waltz-style-background-color

Notice that all letters must be lower case.

The content of the tag should be the expression that will be evaluated in Waltz to determine a value for the style. Keep in mind that values in Waltz may not always be the correct format for a CSS property. In those cases, it may be helpful to use small conditional statements to affect style. For example, to set the background color previously references, you might write:

$.someNode.value > 0 ? '#00ff00' : '#ff0000'

A relatively complete list of CSS properties can be found in Mozilla's CSS Reference, however not all CSS properties are valid. For example, you can not define a @FontFace for reuse in other components, nor can you use vendor-specific prefixed properties like -webkit-border-radius.

Manually Performing Actions

In some situations, it may be desirable to trigger an action from existing JavaScript, instead of allowing Waltz to perform the integration for you. The Waltz JavaScript object provides a function, eval(…), which allows you to evaluate an expression in Waltz, and optionally handle the result and any errors. The error handler, or both the result and error handler can be omitted if desired, but a result handler must be defined to be able to define an error handler.

Keep in mind that some types will not return immediately useful information as their result. For example, the Material type in Waltz is actually converted to a set of JavaScript Canvas drawing instructions, and raster-based data is transmitted as either a Base64 Encoded PNG or Base64 Encoded JPEG, depending on the settings in Waltz.

If you had an existing function in JavaScript and wanted to run a script in Waltz only if some condition is met, you could do the follow:

if (someCondition) {
    Waltz.eval("$.scriptFunction.run()");
}

If you wanted to check a value in Waltz and perform local JavaScript only if that value was greater than 1, you could do the following:

Waltz.eval("$.myValue.value"), function(result) {
    if (result > 1) {
        // Do something in JavaScript
    }
});

Lastly, if you wanted to handle an error in the evaluation specially, you could do the following:

Waltz.eval("$.myValue.value"), function(result) {
    if (result > 1) {
        // Do something in JavaScript
    }
}, function(error) {
    // Do something special to handle the error
});

Advanced Initialization

Two additional forms of the function that initializes Waltz support are available. The first allows you to define the default value for waltzActions when a data-waltz-action-event is not defined. By default, this value is set to `default`, which lets Waltz choose the best standard touch event based on whether you are using a touchscreen or a mouse. For example, if you wanted action, by default, to be bound to the mouseleave action event, you would initialize Waltz support with:

$(function() {
    Waltz.init(30, 'mouseleave');
});

The second available option is forcing communication with Waltz to take place on a specific port. This can be useful for applications like Hype that support local debugging, but run their own web server. Assuming Waltz is running on the default Scene port (port 80), the following would include Waltz from the correct server and initialize Waltz support on that same port:

<script src="http://localhost:80/api/jquery.js"></script>
<script src="http://localhost:80/api/waltz.js"></script>

<script>
    $(function() {
        Waltz.init(30, 'default', 80);
    });
</script>

Notice that we must explicitly also include the scripts from the correct port. Be sure to change this before saving your files for Waltz, as this will only work when using a web server on the same machine as Waltz.