CanvasAnimater.js

CanvasAnimater.js is a utility script for animating, recording, and interacting with graphics using the HTML5 <canvas>. It is light weight, uses vanilla javascript, and has no dependencies.

At its core CanvasAnimater.js provides controlled frame-throttling to render an animation at a user specified frame rate, but its essence offers a way to parametrize dynamic animations by exposing a relative time evolution variable.

Although this tool was developed for recreational creative coding purposes, its main functionality lends itself to any situation where rapid prototyping of Canvas animations is desired. Unlike other popular frameworks that achieve something similar, like p5.js, CanvasAnimater.js does not offer special drawing context primitives for rendering graphics beyond the native Canvas API. Seemingly more cumbersome, working directly with the Canvas API allows deeper control for generating graphics, and in this way CanvasAnimater.js gives us the best of both.

CanvasAnimater.js provides an intuitive interface with declarative style control of settings and the option of more refined functionality. Check out the interactive guide below for basic usage and consult the API reference for details on all available settings and methods.


CanvasAnimater.js is still under active development and changes to the underlying source, API, and documentation are possible.

Future developments include:

  • key commands for starting/stopping animation and recording
  • easing functions for time evolution
  • more interaction modes and properties
  • various file formats for recorded output



Guide



Initialization

Lets begin our explorations of CanvasAnimater by describing a basic setup for initializing and displaying a canvas graphic. In this section, we will construct the foundations of a basic template that we will build upon throughout this guide.

We start by creating a new instance to the CanvasAnimater class using the constructor function CanvasAnimater(canvas_id). Throughout this guide we will call our instantiated CanvasAnimater object canvasAnimater using the "camelCase" convention.

This constructor method takes as input the parameter canvas_id corresponding to the HTML id attribute of a <canvas> element, and sets it as the target for CanvasAnimater. We can target an existing <canvas> element in the DOM by using its attribute id, for example id = 'myCanvas' as done in the example below. Otherwise, a new <canvas> element is created and added to the DOM with id = 'canvas_id'.

let canvasAnimater = new CanvasAnimater('myCanvas');

Once we have created a new CanvasAnimater object, we can start setting up the animation. First, we get the '2D' rendering context of the canvas for drawing onto the canvas. This is readily accessed with the context property of a CanvasAnimater object.

let ctx = canvasAnimater.context;

Now we define and set a function where we specify what to draw onto the canvas. This function must be set to the draw property of our CanvasAnimater object. By referencing our context property in this draw function, we can make use of any methods available from the native interface. To keep it simple for now, lets just draw a black 100x100 rectangle onto the canvas as follows:

canvasAnimater.draw = function () { ctx.fillStyle = 'black'; ctx.fillRect(0, 0, 100, 100); }

Our last step to render and display our canvas requires calling the animate() method. This method should be called at the end of our script after any other configurations to CanvasAnimater are made. Note that this method takes an optional parameter settings_object to configure additional settings, but we will ignore that for now until the next section.

canvasAnimater.animate();

Check out the interactive CodePen below where we put all this together to display a static black square. There is nothing animating here yet, but this template offers a bit of boilerplate code to get set up with CanvasAnimater.



Settings

CanvasAnimater offers a few different ways to configure all of its settings. A complete list of settings can be found in the API reference together with their description and defaults. These settings are exposed as named properties of the CanvasAnimater class, which can be used as a declarative style interface to setup our animation. Alternatively, CanvasAnimater also provides various methods to configure animation settings. Both of these paradigms can be used throughout a CanvasAnimater sketch as we please. The rest of this guide will serve to give further details on all available settings and methods.

One way to configure animation settings is by using the setAnimater(settings_object) method. Here, the input settings_object is an object of key-value pairs corresponding to CanvasAnimater settings. This method can be called throughout our script multiple times with different input settings without overwriting unspecified properties.

As mentioned in the previous section, we can also specify our desired CanvasAnimater settings with a settings_object as an optional parameter to the main animate(settings_object) method.

For example, the following settings_object can be used to configure CanvasAnimater to render a variant of a sketch we will develop later in this guide.

settings_object = { width: 300, height: 300, center_canvas: true, fps: 24, loop_animation: true, t_start: 0, t_end: 2*Math.PI, animation_mode: "time", animation_time: 60, }

The following sketch uses these settings to draw a white disk that moves around in a circle like the second hand of a clock. Note that at the end of our script we call the printSettings() method, which serves as a utility function that prints all configured CanvasAnimater settings to the console.



Canvas Settings

This section will describe canvas settings for sizing, centering, and flexing the canvas. To demonstrate, we will continue to build on the basic sketch template we set up in the previous section.

Sizing

We can set the size of the canvas using the size(width, height) method, which takes input parameters width and height corresponding to the horizontal and vertical dimensions in pixels. Here, we set the canvas size to a square 300x300 pixels:

canvasAnimater.size(300,300);

The width and height properties of a CanvasAnimater object can be used as variables in your sketch. For example, lets use them in our draw function from the last section to make the black rectangle fill the entire canvas as follows:

canvasAnimater.draw = function () { let W = canvasAnimater.width; let H = canvasAnimater.height; ctx.fillStyle = 'black'; ctx.fillRect(0, 0, W, H); }

Our draw function essentially just draws a static black background to the canvas. We still have not animated anything yet, but you can view this in action below and try changing the parameters of size(width, height) to see how the rendered canvas will update.



Centering

By default, the canvas is placed in the top-left corner of the window. We can easily center the canvas in the window using the center(center_canvas) method, which has an optional boolean parameter center_canvas where the canvas is centered if true.

canvasAnimater.center();

Alternatively, the canvas can be centered by setting the center_canvas property to true:

canvasAnimater.center_canvas = true;

Below, we center the canvas using the center() method. Consider changing the size of the canvas and observe how the canvas remains conveniently centered.



Flexing

The flex(flex_canvas) method scales the canvas to the full window dimensions, and automatically rescales whenever the window dimensions are changed. Here, flex_canvas is an optional boolean parameter which defaults to true.

canvasAnimater.flex();

Likewise, the canvas can be flexed by setting the flex_canvas property to true:

canvasAnimater.flex_canvas = true;

If we use the canvas width and height properties in our draw function as we did in our previous examples, we can render appropriately scaled graphics at any aspect ratio. In the CodePen below, try changing the size of the window and observe how the black rectangle's dimensions update to basically serve as a background.



Animating

In this section we will describe the essential animating functionality of CanvasAnimater. This includes frame throttling to render our animation at a fixed frames-per-second, and various modes allowing us to control parametrized time evolution of our animation. In the API reference, you can find all relevant animation settings and methods like FPS(), mode(), evolve(), loop()

Lets begin our exploration with the basic sketch we set up in the previous section, where we simply drew a static black background. To illustrate all the animating functionality, we will now try to animate a white disk that moves around the canvas. First we will present a more traditional method for animating graphics. We can than contrast this and appreciate the utility offered by CanvasAnimater.

The draw function below adds a white disk that is meant to move in a circular path around the center of the canvas. Here we defined the parameters for the disk in terms of the canvas width and height properties in order to present a scale invariant graphic, which we utilize by using the flex() canvas setting. Moreover, we declare global time and rate variables to parameterize the circular motion where time is incremented by rate = 0.01 at the end of our draw function.

let time = 0; let rate = 0.01; canvasAnimater.draw = function () { let W = canvasAnimater.width; let H = canvasAnimater.height; // draw a black background ctx.fillStyle = 'black'; ctx.fillRect(0, 0, W, H); // define a radius for the circular path the disk follows let R = Math.min(W,H)/4; // draw a white disk that moves in a circle ctx.beginPath(); ctx.arc(W/2 + R*Math.sin(time), H/2 - R*Math.cos(time), R/4, 0, 2*Math.PI); ctx.closePath(); ctx.fillStyle = 'white'; ctx.fill(); time += rate; }

CanvasAnimater implements animation by repeatedly calling the draw function to render consecutive frames. Previously in our demo, the draw function was not designed to display any motion since it effectively drew the same plain black background for each frame. Now you can see this new draw function in action below where a disk moves around. Note that our previous boiler plate template for our (non-animated) sketch directly translates over here to yield an animated graphic.



Frame Throttling

We can adjust the frames-per-second at which the animation is rendered using the FPS(fps) method, where fps is a number specifying the render rate.

canvasAnimater.FPS(50);

Equivalently, we can set the fps property of a CanvasAnimater object to our specification:

canvasAnimater.fps = 50;

If left unspecified, the default fps is 50 frames-per-second. However, note that the true rendered rate is ultimately dependent on, and limited to, client-side display and compute subject to the complexities of what we draw. Below we render the same animation as above, but at a slower rate of 24 fps to observe its effect.



Evolving

Having seen how to implement animation with a more traditional approach, lets now learn the functionality built into CanvasAnimater for parameterized time evolution.

Previously, to control the animation we had to declare our own time and rate variables to use in our draw function, which we defined to be parameterless. With CanvasAnimater, we could instead define the draw function to include a single locally-scoped parameter to represent time. By doing so, CanvasAnimater will automatically evolve the time variable and offer additional control of the rendered animation.

Below, we redefine our draw in terms of an arbitrarily named parameter t. Note that we do not declare and set any initial values to the time and rate variables like before.

canvasAnimater.draw = function (t) { let W = canvasAnimater.width; let H = canvasAnimater.height; // draw a black background ctx.fillStyle = 'black'; ctx.fillRect(0, 0, W, H); // define a radius for the circular path the disk follows let R = Math.min(W,H)/4; // draw a white disk that moves in a circle ctx.beginPath(); ctx.arc(W/2 + R*Math.sin(t), H/2 - R*Math.cos(t), R/4, 0, 2*Math.PI); ctx.closePath(); ctx.fillStyle = 'white'; ctx.fill(); }

This set up is enough to recreate our animation exactly as it was before with a FPS of 50. In this case, CanvasAnimater is using some default animation settings which we'll show how to change in the following sections.

When our draw function is defined with a time parameter, we can use evolve(t_start, t_end) to easily set a time evolution range of the animation:

canvasAnimater.evolve(t_start, t_end);

Then for each sequential frame of the animation, CanvasAnimater will automatically increment the time variable in the set range and pass it to draw. Both t_start and t_end are optional parameters for the evolve() method. Their default values are t_start = 0 and t_end = undefined, in which case time is incremented indefinitely in the animation.

Here we set our evolution time range as t_start = 0 and t_end = Math.PI using the method evolve(0, Math.PI). However, note that the evolution time interval can also be set using properties:

canvasAnimater.t_start = 0; canvasAnimater.t_end = Math.PI;

As shown below, this animates our disk clock-wise only half way around its circular path until the time evolution reaches its end and the animation stops. Try experimenting with different time intervals to control the animation. The evolve(t_start, t_end) method can even accept parameters such that t_end < t_start, which can be used to reverse the time evolution.



Looping

An animation can be looped over its evolution time range using the loop(loop_animation) method, which takes an optional boolean parameter loop_animation that defaults to true.

canvasAnimater.loop(true);

Looping can also be enabled by setting the loop_animation property:

canvasAnimater.loop_animation = true;

Lets now loop our animation over its previously set evolution time range. This will make our disk move halfway around its circular path and then appear to jump back to its starting point when the animation loops.



Modes

CanvasAnimater allows us to control our animation's time evolution using various modes named: 'rate', 'frames', and 'time'. The animation mode can be configured using the mode() method, or by setting the animation_mode and related properties as described below.



'rate'

We already got some familiarity with the 'rate' animation mode in our explorations above, since it is the default animation mode offered by CanvasAnimater. Regardless, we can specify this mode by setting the animation_mode property:

canvasAnimater.animation_mode = 'rate';

The main parameter for this mode is given by the animation_rate property, which specifies how much the evolution time variable is incremented each frame. The default value for animation_rate is 0.01, but can be set to any value:

canvasAnimater.animation_rate = 0.03;

Alternatively to animate in 'rate' mode, we can set both the animation_mode and animation_rate using the mode() method as

canvasAnimater.mode('rate', animation_rate);

Here, again animation_rate is an optional parameter defaulting to 0.01, but set to 0.05 in the sketch below making the disk move faster along its path.



'frames'

In the 'frames' animation mode our animation is rendered in a set number of frames independent of the frame rate. We can specify this mode and desired frame number by setting the animation_mode and animation_frames properties:

canvasAnimater.animation_mode = 'frames'; canvasAnimater.animation_frames = 12;

Likewise, we can set up 'frames' mode using the mode() method instead with some optional parameters:

canvasAnimater.mode('frames', animation_frames, animation_rate);

The additional parameter animation_rate is optional and not relevant when the evolution time range is bounded with a defined t_end. This is illustrated in the following example, where we evolve our time parameter from t_start = 0 to t_end = 2*Math.PI so that the disk completes one full circular lap in just a total of 12 frames. To make this more obvious, we set our render rate to only 1 frame-per-second.

If our evolution time range is left unbounded while using the 'frames' animation mode, then time evolves according to animation_rate. However, the animation will still render a finite number of frames specified by animation_frames.

For example, lets again render 12 frames at 1 frame-per-second, but now an animation_rate of Math.PI/2:

canvasAnimater.mode('frames', 12, Math.PI/2);

This jumps the disks a quarter turn along its path each frame for a total of 12. frames.



'time'

By using the 'time' animation mode, we can make our animation run for a specific absolute time. This time is expressed is seconds and given by the animation_time property. Similar to the other modes, this can be set using the animation_mode property:

canvasAnimater.animation_mode = 'time'; canvasAnimater.animation_time = 60;

The mode() method can also be used to set up 'time' by calling

canvasAnimater.mode('time', animation_time, animation_rate);

Again, animation_rate is optional and only used when an evolution time range is not specified. On the other hand, when a finite evolution time range is set, the animation will take animation_time seconds to evolve through its range. This is can be useful if we desire our animation to be a certain fixed time, but also want render at different frames-per-second.

For example below, while rendering at 50 frames-per-second, we make the disk take 60 seconds to move once around in a circle like the second-hand of a clock:

canvasAnimater.mode('time', 60);

If we do not set an end to the evolution time, then animation_rate may be specified to control the evolution, but the total rendered time of the animation will be constrained to animation_time. Below we demonstrate this using:

canvasAnimater.mode('time', 10, 0.03);

This animates the disk with an animation_rate of 0.03, for a total of 10 seconds until it stops.



Interacting

With CanvasAnimater we can easily add real-time user interaction to our canvas animations. In this section we will demonstrate how to use the different interaction modes available, and describe various properties that provide user interaction data. Consult the API reference for all relevant settings and methods, like interact(), interactionOrigin(), and scrollSize().

To initialize any of the interaction modes we can use the interact() method:

canvasAnimater.interact(interaction_modes, interaction_enabled);

Here the latter parameter, interaction_enabled, is an optional boolean used to enable/disable interaction if true/false. interaction_modes is a string, or list of strings, of available interaction modes that specify which modes we want to use.

The available interaction modes are 'mousedownmove', 'mousehovermove', 'touchmove', and 'scroll', described in further detail below. Note that both interaction_modes and interaction_enabled are also properties of CanvasAnimater that can alternatively be used to configure interaction settings, for example:

canvasAnimater.interaction_modes = ['mousedownmove', 'touchmove']; canvasAnimater.interaction_enabled = true;

Depending on which interaction modes we choose, CanvasAnimater provides various interaction properties corresponding to user input data. These properties can be used directly within our draw function's code to control the animation, or in the user defined interaction function. In principle, the same interactivity effects can be accomplished using either approach, but in practice one may be preferred. The main difference is that draw is called for every rendered frame, whereas interaction is only called when any of the initialized event handlers are active. We will demonstrate these different approaches in the following sections.



'mousedownmove'

Interaction Properties:

mouseX, mouseY, mouseX1, mouseY1, pressedMouse, pressed

This section will describe how to use the 'mousedownmove' interaction mode. We will invest some detail going over all relevant interaction properties and settings since their usage translates to other available interaction modes.

The interaction mode 'mousedownmove' enables interactivity whenever the mouse is pressed down and moved on the canvas. We can enable this mode by simply calling:

canvasAnimater.interact('mousedownmove');

When this mode is enabled we can use the interaction properties listed above as CanvasAnimater properties in our sketch. Lets start by demonstrating basic usage of the properties mouseX and mouseY, which provide values of the mouse's horizontal and vertical pixel coordinates, respectively. To illustrate this below, we map the mouseX and mouseY properties to the position of a white disk.

Let's show how to accomplish this in two ways: with and without using the interaction function. First, we declare global variables x_pos and y_pos to represent our disk's position to be used in our draw function:

let x_pos = 0; let y_pos = 0; canvasAnimater.draw = function (t) { let W = canvasAnimater.width; let H = canvasAnimater.height; // draw a black background ctx.fillStyle = 'black'; ctx.fillRect(0, 0, W, H); // draw a white disk at position (x_pos, y_pos) ctx.beginPath(); ctx.arc(x_pos, y_pos , 10, 0, 2*Math.PI); ctx.closePath(); ctx.fillStyle = 'white'; ctx.fill(); }

Next, using the interaction property, we define and set a function to update the disk's position with our interaction data given by mouseX and mouseY:

canvasAnimater.interaction = function () { x_pos = canvasAnimater.mouseX; y_pos = canvasAnimater.mouseY; }

This moves the disk to wherever the user's mouse is pressed down. By moving the mouse over the canvas whenever its pressed , we can effectively drag the disk around. Note that when the sketch is initialized before any user interaction the disk is positioned in the top-left corner of the canvas, which corresponds to the default origin in pixel coordinates.

For reference in the interactive sketches below, we display text on the canvas for the interaction variables in use. This is implemented within our draw function, but can be effectively ignored for our purposes in this guide.

Before moving on, lets recreate the same sketch without using the interaction property as a standalone function. This approach can be more minimal and may not require declaring global variables. Having specified interaction modes, we can simply refer to available interaction properties directly within our draw function, and only declare locally scoped variables if needed as follows:

canvasAnimater.draw = function (t) { // get interaction variables for mouse position let x_pos = canvasAnimater.mouseX; let y_pos = canvasAnimater.mouseY; let W = canvasAnimater.width; let H = canvasAnimater.height; // draw a black background ctx.fillStyle = 'black'; ctx.fillRect(0, 0, W, H); // draw a white disk at mouse position ctx.beginPath(); ctx.arc(x_pos, y_pos , 10, 0, 2*Math.PI); ctx.closePath(); ctx.fillStyle = 'white'; ctx.fill(); }

The sketch below achieves the same interactivity in moving the disk around with the mouse. For the rest of this guide, we will use either approach as it suits our needs.

When the 'mousedownmove' mode is enabled, CanvasAnimater exposes the boolean interaction property pressedMouse, which is true whenever the mouse is pressed down and false otherwise. For this mode, the more general property pressed behaves similarly, which also responds when the 'touchmove' interaction mode is enabled as discussed further below.

The following sketch uses pressedMouse to change the disk's color to red whenever the mouse is pressed. Here we utilize this property directly in our draw function:

canvasAnimater.draw = function (t) { // get interaction variables for mouse position let x_pos = canvasAnimater.mouseX; let y_pos = canvasAnimater.mouseY; let W = canvasAnimater.width; let H = canvasAnimater.height; // draw a black background ctx.fillStyle = 'black'; ctx.fillRect(0, 0, W, H); // draw a white disk at mouse position ctx.beginPath(); ctx.arc(x_pos, y_pos , 10, 0, 2*Math.PI); ctx.closePath(); // make disk red if mouse is pressed and white otherwise if (canvasAnimater.pressedMouse) { ctx.fillStyle = 'red'; } else { ctx.fillStyle = 'white'; } ctx.fill(); }

The interaction properties mouseX1 and mouseY1 behave like their counterparts mouseX and mouseY, but give the mouse's positional coordinates scaled to unit range. For example, suppose our canvas has a size of 500x300 pixels. Then mouseX and mouseY numerically range from 0 to 500 and 0 to 300, respectively. However, mouseX1 and mouseY1 will both range from 0 to 1.

In the following sketch, we draw a gray box at the top-left corner of the canvas. We then use mouseX1 and mouseY1 to bound the disk's position to remain inside the gray box. Observe that the disk reaches the edges of the box precisely when the mouse reaches the edge of the canvas.

canvasAnimater.draw = function (t) { // get interaction variables for normalized mouse position let x_pos = canvasAnimater.mouseX1; let y_pos = canvasAnimater.mouseY1; let W = canvasAnimater.width; let H = canvasAnimater.height; // draw a black background ctx.fillStyle = 'black'; ctx.fillRect(0, 0, W, H); // declare a side length of the box let B = 0.75*Math.min(W,H); // draw a gray box in the top-left corner ctx.fillStyle = 'gray'; ctx.fillRect(0, 0, B, B); // draw a white disk at mouse position bounded to the box ctx.beginPath(); ctx.arc(B*x_pos, B*y_pos , 10, 0, 2*Math.PI); ctx.closePath(); ctx.fillStyle = 'white'; ctx.fill(); }

When working with spatial coordinates on the canvas we usually consider the top-left corner pixel of the canvas as the origin of our coordinate axis, and reference other locations relative to this point. This convention is used by default for the mouse position properties mouseX, mouseY, mouseX1, and mouseY1.

We can change the relative location of this origin point using the interactionOrigin(x0, y0) method, where the input parameters (x0, y0) represent the origin point in a scale-invariant manner. Both x0 and y0 are numbers ranging from 0 to 1 that specify a relative origin point on the canvas. The point (0, 0) corresponding to the top-left corner of the canvas, and is the default interaction origin as mentioned. To get our bearings, the other corners are given by: (1, 0) the top-right, (1, 1) the bottom-right, and (0, 1) the bottom-left. In this way, the point (0.5, 0.5) corresponds to the center of the canvas:

canvasAnimater.interactionOrigin(0.5,0.5);

Note that we can also set the interaction origin using the interaction_origin property, where we specify the point as an array of two numbers:

canvasAnimater.interaction_origin = [0.5,0.5];

When the interaction origin is at its default point (0,0) corresponding to the top-left corner of the canvas, our mouse interaction data is effectively limited to a single positive quadrant. Consequently, their numerical values are all positive. However, when we set a interaction origin to something other than (0,0), the interaction data spans all four quadrants allowing negative values. We will demonstrate this in the following sketch.

Below we redo our previous sketch with the disk bounded to the gray box, but now center the box and interaction origin by setting it to (0.5, 0.5). Then we use the unit scaled interaction data mouseX1 and mouseY1 like before to bound the disk's position.

canvasAnimater.draw = function (t) { // get interaction variables for normalized mouse position let x_pos = canvasAnimater.mouseX1; let y_pos = canvasAnimater.mouseY1; let W = canvasAnimater.width; let H = canvasAnimater.height; // draw a black background ctx.fillStyle = 'black'; ctx.fillRect(0, 0, W, H); // declare a side length of the box let B = 0.75*Math.min(W,H); // draw a gray box centered in the canvas ctx.fillStyle = 'gray'; ctx.fillRect(W/2 - B/2 , H/2 - B/2, B, B); // draw a white disk at mouse position bounded to the box ctx.beginPath(); ctx.arc(W/2 + B/2*x_pos, H/2 + B/2*y_pos , 10, 0, 2*Math.PI); ctx.closePath(); ctx.fillStyle = 'white'; ctx.fill(); }

In the sketch below, we display both pairs of mouse interaction data for reference. Observe how their values can now be negative, and how the unit scaled variables are now bound within the range -1 and 1.



'mousehovermove'

Interaction Properties:

mouseX, mouseY, mouseX1, mouseY1

The interaction mode 'mousehovermove' enables interactivity whenever the mouse is hovered over the canvas. This mode is similar to the 'mousedownmove' mode, but the mouse no longer needs to be pressed down to expose the interaction variables mouseX, mouseY, mouseX1, and mouseY1. However, unlike the 'mousedownmove' mode, the boolean properties pressedMouse or pressed no longer respond to the 'moushovermove' mode.

We can enable the 'mousehovermove' interaction mode using the interact() method:

canvasAnimater.interact('mousedownmove');

Below we demonstrate the same sketch we explored that moves a disk, but now using 'mousehovermove' instead. Consult the 'mousedownmove' section for details on using properties mouseX1, and mouseY1 and the interactionOrigin() method.



'touchmove'

Interaction Properties:

mouseX, mouseY, mouseX1, mouseY1, pressedTouch, pressed

The interaction mode 'touchmove' is specifically used for enabling interactivity for touch screen interfaces. This mode is similar to the 'mousedownmove' and 'mousehovermove' modes. However, our previous sketches showcasing the mouse interaction functionally using those modes did not respond to any touch interaction. For a consistent user experience, we can enable 'touchmove' together with a mouse-based mode. Note that if we want to specify multiple modes using interact(), then the input parameter must be given as an array of interaction mode strings:

canvasAnimater.interact(['mousedownmove', 'touchmove']);

The 'touchmove' mode also shares the same mouse position interaction variables like mouseX, mouseY, mouseX1 and mouseY1. In addition, whenever touch is engaged the boolean properties pressedTouch and pressed are exposed. The latter is true when either pressedTouch or pressedMouse are true. Consult the 'mousedownmove' section for related details on using these properties and the interactionOrigin() method.

In the sketch below, we utilize both the 'touchmove' and 'mousedownmove' interaction modes to move the disk with either mouse or touch interactivity.



'scroll'

Interaction Properties:

scrollX, scrollY, scrollX1, scrollY1

The 'scroll' interaction mode exposes scroll bar parameters for use in our sketch. When the 'scroll' mode is enabled the canvas is automatically wrapped and fixed in its containing window. Although the actual canvas position in the window is not altered when scrolling, this creates a virtual scroll simulating a swiping effect.

We can initialize the 'scroll' interaction mode with the interact() method:

canvasAnimater.interact('scroll');

Once initialized, the interaction properties scrollX and scrollY give us the horizontal and vertical components of the windows scroll position in pixels. By default, CanvasAnimater sets the available scroll size to 2000 pixels in both the horizontal and vertical directions. We can specify our own scroll size using the scrollSize(scroll_width, scroll_height) method:

canvasAnimater.scrollSize(2000, 2000);

Alternatively, we can use th CanvasAnimater properties scroll_width and scroll_height to set the scroll size. For example, to utilize only a vertical scroll we set the width to 0 pixels:

canvasAnimater.scroll_width = 0; canvasAnimater.scroll_height = 2000;

In the sketch below, we map the interaction properties scrollX and scrollY to the position of the white disk allowing us to control it through scrolling commands. Note that we bound the values of these mapped positional coordinates to ensure the disk stays within the canvas.

canvasAnimater.draw = function (t) { let W = canvasAnimater.width; let H = canvasAnimater.height; // get interaction variables for scroll position and bound their range let x_pos = Math.min(W,canvasAnimater.scrollX); let y_pos = Math.min(H, canvasAnimater.scrollY); // draw a black background ctx.fillStyle = 'black'; ctx.fillRect(0, 0, W, H); // draw a white disk at scroll position ctx.beginPath(); ctx.arc(x_pos, y_pos , 10, 0, 2*Math.PI); ctx.closePath(); ctx.fillStyle = 'white'; ctx.fill(); }

The interaction properties scrollX1 and scrollY1 behave like their counterparts scrollX and scrollY, but have their values scaled to unit range between 0 and 1. These are analogous to the unit-scaled properties mouseX1 and scrollY1 we explored for the other interaction modes.

Here we recreate our previous sketch, this time using scrollX1 and scrollY1 to bound the disks position on the canvas.

canvasAnimater.draw = function (t) { let W = canvasAnimater.width; let H = canvasAnimater.height; // get interaction variables for scroll position let x_pos = canvasAnimater.scrollX1; let y_pos = canvasAnimater.scrollY1; // draw a black background ctx.fillStyle = 'black'; ctx.fillRect(0, 0, W, H); // draw a white disk at scroll position ctx.beginPath(); ctx.arc(W*x_pos, H*y_pos , 10, 0, 2*Math.PI); ctx.closePath(); ctx.fillStyle = 'white'; ctx.fill(); }



Dwitter Mode

CanvasAnimater offers special functionality for implementing sketches coded on Dwittter.net, a popular "code golf" platform. One of the main objectives on Dwitter is to use very little code, typically under 140 bytes. To this end, users are provided with a few common functions named with just a single character to utilize in their sketch. These function definitions are:

t: Elapsed time. S: Shorthand for Math.sin. C: Shorthand for Math.cos. T: Shorthand for Math.tan. R: Function that generates rgba-strings; usage: R(255, 255, 255, 0.5) c: The canvas. x: The 2D context for that canvas.

By calling the dwitterMode() method, CanvasAnimater automatically creates a namespace to interpret code using these short function definitions from Dwitter. We are then free to use them anywhere in our draw function. For example, here we recreate the same sketch we explored in the Evolving section of a disk moving around in a circular path. Note that we can still conveniently use CanvasAnimater properties as well.

canvasAnimater.draw = function (t) { let W = canvasAnimater.width; let H = canvasAnimater.height; // draw a black background x.fillStyle = R(0, 0, 0, 1); x.fillRect(0, 0, W, H); // define a radius for the circular path the disk follows let D = Math.min(W,H)/4; // draw a white disk that moves in a circle x.beginPath(); x.arc(W/2 + D*S(t), H/2 - D*C(t), D/4, 0, 2*Math.PI); x.closePath(); x.fillStyle = R(255, 255, 255, 1); x.fill(); }

In our sketch below, we enable Dwitter mode by simply calling dwitterMode() without specifying any of its optional parameters for now.

By convention, sketches on Dwitter are drawn onto a fixed canvas of size 1920x1080 pixels. Therefore, in order to suit this aspect ratio, it is common for users to hard code numerical values directly into their sketch that determine the desired geometry. When we implement a Dwitter sketch with CanvasAnimater we can choose to preserve the native Dwitter canvas size, or rescale the sketch to user specified dimensions. This is achieved using optional boolean parameters supplied to the dwitterMode() method:

canvasAnimater.dwitterMode(dwitter_mode, dwitter_scale, dwitter_res);

Here, dwitter_mode defaults to true and simply enables/disables Dwitter mode. The remaining two parameters both default to false, but when true yield a rendering more faithful to Dwitter's. When dwitter_res is true the canvas size is set to 1920x1080 pixels, overriding any other canvas sizing commands set by CanvasAnimater. When dwitter_scale is true, the canvas is rescaled to user specified dimensions by automatically transforming the canvas context.

To explore these settings, lets borrow the code for the default sample sketch on Dwitter:

'for(i=0;i<9;i++)x.fillRect(400+i*100+S(t)*300,400,50,200)'

As done previously, we can just use this code verbatim within our draw function, but instead here we will introduce and set the CanvasAnimater property dwitter_code to our code string as follows;

canvasAnimater.dwitterCode = 'for(i=0;i<9;i++)x.fillRect(400+i*100+S(t)*300,400,50,200)';

This approach offers a more stripped down template to conveniently import a Dwitter sketch. Now by calling dwitter_mode with all optional parameters set to true, our canvas dimensions and sketch geometry scale accordingly. This is demonstrated in the following sketch, where we use the flex() method to make the canvas fill an arbitrarily sized window.



Recording

Recording our animation is easily accomplished with CanvasAnimater. This outputs individual frames for download that can be recompiled into various media formats. Once we have our sketch set up, we can record the animation with all the same settings to ensure a faithful rendering. This is done by calling the record() method with some optional parameters:

canvasAnimater.record(record_enabled, filename, record_loops, pre_loop);

Each of these input parameters are also CanvasAnimater properties that can be alternatively used to configure recording settings.

The first parameter record_enabled is a boolean which defaults to true. This allows us to conveniently toggle on/off recording when the animation is rendered.

The second parameter filename serves as a name prefix to give each recorded frame, where output frames are indexed as: 'filename_0', 'filename_1', 'filename_2', etc. The default filename string is 'frame'.

The last two parameters apply only when our animation is set to loop. In this case, record_loops is the number of animation loops to be recorded, and defaults to 1. The boolean parameter pre_loop defaults to true, which allows one full animation loop to complete before recording begins.

Unless our animation time is unbounded, the number of frames to be recorded is automatically determined according to our animation settings. However, we can set a limit to the number of recorded output frames using the recordMaxFrames() method:

canvasAnimater.recordMaxFrames(record_max_frames);

The input parameter record_max_frames is the number of maximum frames that can be recorded. It has a default value of 1000 frames, and can also be set as a CanvasAnimater property.

The template below offers a recording setup for one of the sketches we experimented with earlier in this guide, where a white disk moves around in a circle. Here our animation is designed to loop seamlessly, but we opt to record just 1 animation loop with pre_loop set to true. Moreover, we give our output filename the prefix 'myFrame_' :

canvasAnimater.record(false, 'myFrame_', 1, true);

Note that we initialize the sketch with record_enabled set to false to avoid any unwarranted downloading while viewing this guide. For this particular animation with these settings, CanvasAnimater will output 100 recorded frames.