Tutorial

Custom Behaviors

Overview

Ayva's do() method accepts behaviors in the form of a function, or an object with a perform() method. Upon calling do(), the Ayva instance will start a loop that continuously invokes the provided function (or perform() method). After each invocation it will wait for any moves started by the behavior to complete before starting the next iteration. The loop will continue forever until either do() is called with another behavior, stop() is called, or the behavior completes itself.

Function

const myStroke = function (ayvaInstance) {
    ayvaInstance.$.stroke(0, 1).execute(); // Stroke down.
    ayvaInstance.$.stroke(1, 1).execute(); // Stroke up.
};

ayva.do(myStroke); // Perform the behavior until commanded to stop.

Try it out!

A behavior is independent of any specific instance of Ayva. When a particular Ayva instance performs the behavior, it passes itself as the sole parameter to the function. In the previous example we named this parameter ayvaInstance just to make it clear that it is not necessarily the same instance of Ayva used later in the example. If your program is only ever using one (global) instance of Ayva, you don't need to specify it as a parameter to your function. The following would also work:

ayva.do(() => {
  ayva.$.stroke(0, 1).execute(); // Stroke down.
  ayva.$.stroke(1, 1).execute(); // Stroke up.
}); 

Object Oriented Programming

You can package your behavior into a class that implements the perform() method to make it more reusable and/or configurable. Here is a reimplementation of the previous example using OOP to allow setting the speed of the stroke:

class MyStroke {
  constructor (speed) {
    this.speed = speed;
  }

  perform (ayva) {
    ayva.$.stroke(0, this.speed).execute();
    ayva.$.stroke(1, this.speed).execute();
  }
}

ayva.do(new MyStroke(2)); // Perform a 2 strokes per second stroke.

Try it out!

Completion

By default, Ayva will perform a behavior forever until commanded to stop. A behavior can however signal its own completion if desired. It can do this by setting its complete property to true. Here is a behavior that only strokes for five seconds and then stops (using performance.now() to keep track of time):

class FiveSecondStroke {
  constructor (speed) {
    this.speed = speed;
  }

  perform (ayva) {
    if (!this.startTime) {
      this.startTime = performance.now();
    }

    if (performance.now() - this.startTime < 5000) {
      ayva.$.stroke(0, this.speed).execute();
      ayva.$.stroke(1, this.speed).execute();  
    } else {
      // Five seconds has elapsed, so signal completion.
      this.complete = true;
    }
  }
}

ayva.do(new FiveSecondStroke(2)); // Perform a 2 strokes per second stroke for five seconds.

Note: Ayva includes a convenience class for dealing with durations called VariableDuration. See the API Documentation to think of other ways you might implement this example.

Try it out!

For simple behaviors that consist of only moves, passing a function or creating an object or class that implements the perform() method might be enough. However, for more complex behaviors that require logic based on the current state, have pauses, or depend on the actions of sub behaviors, the asynchronous nature of the Motion API requires special handling. This is discussed in the next section.