Tutorial

Classic Stroke

What is a Classic Stroke?

So named for its timelessness. The OG stroke. It commands Ayva to perform a simple up and down movement on the stroke axis with some (optional) variation on a few parameters such as speed, positions, shape, and twist. Its been often stated that many people are satisfied with just the stroke and twist axis. If you're one of those people, ClassicStroke has got you covered!

Setup

ClassicStroke is available as part of the standard distribution, but to use it within an ES6 module, you must import it. This can be done at the same time that you import the Ayva class. For example, in a browser:

import { Ayva, ClassicStroke } from 'https://unpkg.com/ayvajs';

or from within a Node.js app:

import { Ayva, ClassicStroke } from 'ayvajs';

Once ClassicStroke is imported, you can create new strokes using ClassicStroke's constructor:

// Create a new stroke with default parameters.
const myStroke = new ClassicStroke();

// Perform the stroke until commanded to do otherwise.
ayva.do(myStroke);

Parameters

To customize ClassicStroke's behavior, a configuration object can be passed to the constructor with any of the following parameters:

bottom
Type: Number | Array | Function

The bottom of the stroke range. This can be an absolute value, an array of values, or a function that provides the value for each stroke.
Defaults to 0

// Specify as absolute value.
ayva.do(new ClassicStroke({
  bottom: 0.5,
}));

Try it out!

// Specify as array of values (this pattern will repeat).
ayva.do(new ClassicStroke({
  bottom: [0, 0.25, 0.5, 0.25]
}));

Try it out!

// Specify as a function (this example generates a random value between 0 and 0.5 for each stroke).
ayva.do(new ClassicStroke({
  bottom: () => Ayva.map(Math.random(), 0, 1, 0, 0.5)
}));

Try it out!

top
Type: Number | Array | Function

The top of the stroke range. This can be an absolute value, an array of values, or a function that provides the value for each stroke.
Defaults to 1

// Specify as absolute value.
ayva.do(new ClassicStroke({
  top: 0.75,
}));
// Specify as array of values (this pattern will repeat).
ayva.do(new ClassicStroke({
  top: [1, 0.75, 0.5, 0.75]
}));
// Specify as a function (this example generates a random value between 0.5 and 1 for each stroke).
ayva.do(new ClassicStroke({
  top: () => Ayva.map(Math.random(), 0, 1, 0.5, 1)
}));
speed
Type: Number | Array | Function

The speed of the stroke. This can be an absolute value, an array of values, or a function that provides the value for each stroke.
Defaults to 1

// Specify as absolute value.
ayva.do(new ClassicStroke({
  speed: 1.5,
}));
// Specify as array of values (this pattern will repeat)
ayva.do(new ClassicStroke({
  speed: [0.5, 1, 1.5, 1 ]
}));
// Specify as a function (this example generates a speed that varies with sin)
ayva.do(new ClassicStroke({
  speed: (index) => {
    const x = index / 20; // One cycle every 20 strokes.

    const minSpeed = 0.5;
    const maxSpeed = 3;
    return Ayva.map(Math.sin(x * Math.PI * 2), -1, 1, minSpeed, maxSpeed);
  }
}));

Try it out!

duration
Type: Number | Array | Function

Alternative to speed—the duration of each stroke. This can be an absolute value, an array of values, or a function that provides the value for each stroke. Defaults to undefined (incompatible with speed property).

shape
Type: Function | Array

The "shape" of the stroke. This can be a ramp function, or an even lengthed array of ramp functions. When specified as an array, the even indexed array entries correspond with up strokes, and odd index entries with down strokes.
Defaults to Ayva.RAMP_COS

// Specify as function (ramp)
ayva.do(new ClassicStroke({
  shape: Ayva.RAMP_PARABOLIC
}));

Try it out!

// Specify as array of values (this pattern will repeat)
ayva.do(new ClassicStroke({
  shape: [ Ayva.RAMP_NEGATIVE_PARABOLIC, Ayva.RAMP_PARABOLIC, Ayva.RAMP_COS, Ayva.RAMP_LINEAR ]
}));

Try it out!

relativeSpeeds
Type: Array

An even lengthed array of factors to scale stroke speeds by. The even indexed array entries correspond with up strokes, and odd index entries correspond with down strokes.
Defaults to [1, 1]

// Specify as array of values (this pattern will repeat)
ayva.do(new ClassicStroke({
  speed: 2,

  // Multiply base speed (2) by these factors on corresponding strokes.
  relativeSpeeds: [ 0.5, 0.75, 0.75, 1 ] 
}));

Try it out!

suck
Type: Number

The suck algorithm value.
Defaults to null (i.e. no suck algorithm).

ayva.do(new ClassicStroke({
  // Set suck algorithm to close valve to 0.8 on the up strokes.
  suck: 0.8 
}));
twist
Type: Object

A configuration object for a cos shaped twist with the following parameters:

from - position of the twist axis when at the bottom of a stroke.
to - position of the twist axis when at the top of a stroke.
phase - phase of the cos wave in multiples of π/2
ecc - the eccentricity of the cos wave

Defaults to null (i.e. no twist).

ayva.do(new ClassicStroke({
  twist: {
    from: 0.5,
    to: 1,
    phase: 1,
    ecc: 1
  }
}));

Try it out!

pitch
Type: Object

A configuration object for a cos shaped pitch with the following parameters:

from - position of the pitch axis when at the bottom of a stroke.
to - position of the pitch axis when at the top of a stroke.
phase - phase of the cos wave in multiples of π/2
ecc - the eccentricity of the cos wave

Defaults to null (i.e. no pitch).

ayva.do(new ClassicStroke({
  pitch: {
    from: 0.5,
    to: 1,
    phase: 1,
    ecc: 1
  }
}));

Try it out!

All Together Now

All of these parameters can be used together to create a unique experience:

ayva.do(new ClassicStroke({
  // Random bottom
  bottom: () => Ayva.map(Math.random(), 0, 1, 0, 0.3),

  // Random top
  top: () => Ayva.map(Math.random(), 0, 1, 0.7, 1),

  // Speed vary by sin
  speed: (index) => {
    const x = index / 20; // One cycle every 20 strokes.

    const minSpeed = 0.5;
    const maxSpeed = 3;
    return Ayva.map(Math.sin(x * Math.PI * 2), -1, 1, minSpeed, maxSpeed);
  },

  // "Bouncy" motion by using parabolic ramps
  shape: [ Ayva.RAMP_NEGATIVE_PARABOLIC, Ayva.RAMP_PARABOLIC ],

  // Make up strokes perform at half speed.
  relativeSpeeds: [0.5, 1],

  // With some twist motion slightly out of phase.
  twist: {
    from: 0.25,
    to: 0.75,
    phase: 0.5
  }  
}));

Try it out!

Warning:ClassicStroke does not manipulate the values of any axis but the stroke, pitch, and twist axes (the latter two only if configured). You must therefore take care to orient other axes to the values you would like them to be for the duration of the stroke. The following example demonstrates the effect of not doing this. 😅

// Move the roll and pitch axis way off.
ayva.$.roll(0, 1).pitch(0, 1).execute().then(() => {
  // This stroke will occur without changing the pitch or roll back!
  // Probably not what you want...
  ayva.do(new ClassicStroke());
});

Try it out!

Shorthand

For simple strokes, you do not have to pass a configuration object. The constructor of ClassicStroke accepts the bottom, top, speed, and shape properties directly; with each being optional and set to appropriate default values. Ex:

// A parabolic stroke with bottom = 0, top = 1, and speed = 2.
new ClassicStroke(0, 1, 2, Ayva.RAMP_PARABOLIC);

See the API Documentation for additional details.