# Easing

Easing is a form of interpolation used in animated and interactive systems for controlling the movement of an object between control points that mark the beginning and end of a movement. At it's simplest, by moving a point a fraction of the total distance each frame, a shape can appear to accelerate (or decelerate) as it approaches a target location. Here's a simple example of easing:

• Show Sketch
``````/** @peep sketchcode */
float x; // Current x-coordinate
float y; // Current y-coordinate
float targetX; // Destination x-coordinate
float targetY; // Destination y-coordinate
float easing; // Size of each step along the path

void setup() {
size(200, 200);
background(0);
noStroke();
// Set the initial position
x = 20;
y = 20;
// Set the final/target position
targetX = width - 20;
targetY = height - 20;
// Adapt the position by 1% each update
easing = 0.01;
}

void draw() {
// Fade the background
fill(0, 12);
rect(0, 0, width, height);
// If the current position is more than 1 pixel away from target
if (dist(x, y, targetX, targetY) > 1.0) {
// Update the current position towards the target position
x += (targetX - x) * easing;
y += (targetY - y) * easing;
}
// Draw an ellipse at the current position
fill(255);
ellipse(x, y, 20, 20);
}``````

The core of the above sketch is the following lines that update the current values for `x` and `y` towards `targetX` and `targetY`:

``````x += (targetX - x) * easing;
y += (targetY - y) * easing;``````

Experiment with the value assigned to `easing` to see the effect that this has on the movement of the ellipse.

This example of easing has a simple `if` statement to determine if the values for `x` and `y` still need to be updated, i.e., if the distance between the current position and the target position is greater that `1` pixel.

Add an `else` clause to the conditional to set random values for `targetX` and `targetY` when the `if` test fails. Slow down the rate of fading (using the modulo technique from the previous tutorial) to achieve something like the following: Easing isn't limited to animating the positions of drawn elements, it can be applied to animate any floating point value smoothly.

Extend the sketch you created above to add a variable, e.g., `radius`, to control the size of the circle and another variable, e.g., `targetRadius`, to hold a value that the size should ease towards. Add a line to the body of the `if` statement that updates the value of `radius` each time `draw()` is called in the same way that `x` and `y` are updated. Add another line to the `else` clause that you created for the last sketch to choose a new random size for the circle to ease towards. You should be able to produce a drawing something like the following: How can you update the test performed by the `if` statement to ensure that the circle is sufficiently close in both position and size before choosing new target values?

Continue to adapt your sketch to ease the colour that the shape is drawn in. You can do this either by easing the values for one or more of the red, green and blue components, or (if you set the colour mode appropriately) the hue, saturation and brightness used to set the fill colour. Here is an example of choosing a target hue at random: If new values of the target hue are chosen by adding a random amount to the current hue, a slightly more subtle effect can be achieved: # Easing Functions

So far, we have been easing floating point values, which is possible with a single line of code. Easing between integer values poses a slightly more complex problem because as difference between the values gets small the amount calculated by the easing becomes less than 1 and so doesn't affect the integer value. We can write a function to make sure that we account for this when easing integers:

``````int easeInt(int src, int dst, float easing) {
if (src == dst) return dst; // If no change is required, return
float ease = (dst - src) * easing;
if (abs(ease) < 1) ease /= abs(ease); // Make the value of ease at least 1
return int(src + ease);
}``````

The `easeInt()` function that ensures that the returned value is either the desired destination value, `dst`, or at least 1 more or less that the given value `src`.

## Easing Colours

Given this function we can also write a function that makes it simple to ease between the values stored in a `color` variable. In the previous chapter you will have used variables that stored `float` values and only converted them to a color when used by a `fill()` function. The following functions use `easeInt()` to ease between color values stored as RGB or HSB.

``````color easeRGB(color src, Color dst, float easing) {
return color(easeInt(int(red(src)), int(red(dst)), easing),
easeInt(int(green(src)), int(green(dst)), easing),
easeInt(int(blue(src)), int(blue(dst)), easing));
}

color easeHSB(color src, Color dst, float easing) {
return color(easeInt(int(hue(src)), int(hue(dst)), easing),
easeInt(int(saturation(src)), int(saturation(dst)), easing),
easeInt(int(brightness(src)), int(brightness(dst)), easing));
}``````

## Easing the Background

The following sketch uses easing to update all of the pixels in the current display window so that they fade towards a desired colour, i.e., so that they fade towards a background colour.

• Show Sketch
``````/** @peep sketchcode */
color orange = color(204, 102, 0);
color lightBlue = color(66, 168, 237);
color darkBlue = color(0, 102, 153);

void setup() {
size(200, 200);
background(orange);
noStroke();
}

void draw() {
// Ease the background pixels towards orange
easeBackground(orange, 0.05);
// Draw some fancy squiggly things... (coming in Tutorial 9.3)
translate(width/2, height/2);
for (int i = 0; i < 10; i++) {
float nx = 2 * width * (noise((frameCount + i*1937) * 0.013) - 0.5);
float ny = 2 * height * (noise((frameCount + i*1937) * 0.017) - 0.5);
if (i % 2 == 0) { fill(lightBlue); } else { fill(darkBlue); }
ellipse(nx, ny, 10, 10);
}
}

/** Ease all of the pixels in the display window towards a given colour.
*  Only pixels that are different in colour from the desired colour are
*  updated, making it (reasonably) efficient. The use of easeInt() to
*  calculate the new pixel values ensures that the easing will result in
*  the desired colour and not get stuck at a shade just lighter or darker
*  than it.
*/
void easeBackground(color dst, float easing) {
int dstR = int(red(dst));
int dstG = int(green(dst));
int dstB = int(blue(dst));
loadPixels();
for (int i = 0; i < pixels.length; i++) {
color src = pixels[i];
if (src != dst) {
pixels[i] = color(easeInt(int(red(src)), dstR, easing),
easeInt(int(green(src)), dstG, easing),
easeInt(int(blue(src)), dstB, easing));
}
}
updatePixels();
}

/** Ease an integer value from src towards dst, taking care to only change the
*  value if the difference between src and dst is greater than one, and
*  (if a change is needed) always changing by at least 1.
*/
int easeInt(int src, int dst, float easing) {
if (src == dst) return dst; // If no change is required, return
float ease = (dst - src) * easing;
if (abs(ease) < 1) ease /= abs(ease); // Make the value of ease at least 1
return int(src + ease);
}``````

This approach has the advantage over the previous technique that we've used to fade drawings to a background colour, using a transparent rectangle, that it will not leave any residue of the drawing thanks to the use of `easeInt()`. The disadvantage of this approach is that it is more computationally expensive because the `easeBackground()` function has to check every pixel. So, as the sketch gets larger the function will take longer.

Experiment with the above sketch. Try to create a sketch that will changes the target background colour to one of the three palette colours every 100 frames. The easiest way to achieve this is to put all of the colours in an array, e.g., called `palette` and maintain a separate variable for the current target background colour, which can be changed to a random member of the palette array every 100 frames. Because this will affect every pixel in the sketch, it will show what sort of impact this has on animation speed, to maintain a good frame rate you may want to reduce your sketch size to be less than 200x200 pixels (depending on the speed of your computer). More likely, to ensure that the sketch runs at a consistent speed (not speeding up and slowing down with the changes in the background colour) then you'll also want to use the `frameRate()` function to limit the maximum number of frames per second to an amount that can be achieved even if all of the pixels must be updated, e.g., `frameRate(10)`.

## Easing Pixels

We can use a similar approach to the one above to fade a drawing towards a background image. The code for `easeBackground()` is quite similar but instead of taking a `color` parameter it takes a `PImage` parameter, and it calculates the value for `dst` from each pixel in the destination image, `dstImage`. Running this sketch you'll see that the easing function does a good job of making sure that all traces of the drawing are faded to the background image.

• Show Sketch
``````/** @peep sketchcode */
/* @pjs preload="/uploads/1/jacaranda.jpg"; */
color[] palette = { color(204, 102, 0), color(66, 168, 237), color(0, 102, 153) };

PImage tree;

void setup() {
size(200, 200);
noStroke();
tree = loadImage("/uploads/1/jacaranda.jpg");
background(tree);
}

void draw() {
easeBackground(tree, 0.01);
// Draw some fancy squiggly things... (coming in Tutorial 9.3)
translate(width/2, height/2);
for (int i = 0; i < 10; i++) {
float nx = 2 * width * (noise((frameCount + i*1937) * 0.013) - 0.5);
float ny = 2 * height * (noise((frameCount + i*1937) * 0.017) - 0.5);
fill(palette[i % palette.length]);
ellipse(nx, ny, 10, 10);
}
}

void easeBackground(PImage dstImage, float easing) {
if (width != dstImage.width || height != dstImage.height) return;
loadPixels();
dstImage.loadPixels();
for (int i = 0; i < pixels.length; i++) {
color src = pixels[i];
color dst = dstImage.pixels[i];
if (src != dst) {
pixels[i] = color(easeInt(int(red(src)), int(red(dst)), easing),
easeInt(int(green(src)), int(green(dst)), easing),
easeInt(int(blue(src)), int(blue(dst)), easing));
}
}
updatePixels();
}

int easeInt(int src, int dst, float easing) {
if (src == dst) return dst; // If no change is required, return
float ease = (dst - src) * easing;
if (abs(ease) < 1) ease /= abs(ease); // Make the value of ease at least 1
return int(src + ease);
}``````

We can use a similar approach to the one above for easing between images. In this sketch two images are loaded into an array and a third image is created in memory. A copy of one of the images loaded into the is made to the image in memory and a fourth `PImage` variable is created to point to the image that it should ease towards. Every 100 frames the destination image that the sketch should fade towards is changed.

• Show Sketch
``````/** @peep sketchcode */
/* @pjs preload="/uploads/1/jacaranda.jpg, /uploads/1/kitty.jpg"; */
PImage[] images;
PImage img;
PImage dst;

void setup() {
size(200, 300);
images = new PImage;
images = loadImage("/uploads/1/jacaranda.jpg");
images = loadImage("/uploads/1/kitty.jpg");
img = createImage(images.width, images.height, RGB);
img.copy(images, 0, 0, images.width, images.height, 0, 0, img.width, img.height);
dst = images;
}

void draw() {
if (frameCount % 100 == 0) { // If we've complete 100 updates, switch the image we're fading to
if (dst == images) { dst = images; } else { dst = images; }
}
// Ease the pixels
easePixels(img, dst, 0.04);
image(img, 0, 0);
image(images, 0, 200, 100, 100);
image(images, 100, 200, 100, 100);
}

void easePixels(PImage srcImage, PImage dstImage, float easing) {
if (srcImage.width != dstImage.width || srcImage.height != dstImage.height) return;
srcImage.loadPixels();
dstImage.loadPixels();
for (int i = 0; i < srcImage.pixels.length; i++) {
color src = srcImage.pixels[i];
color dst = dstImage.pixels[i];
if (src != dst) {
srcImage.pixels[i] = color(easeInt(int(red(src)), int(red(dst)), easing),
easeInt(int(green(src)), int(green(dst)), easing),
easeInt(int(blue(src)), int(blue(dst)), easing));
}
}
srcImage.updatePixels();
}

int easeInt(int src, int dst, float easing) {
if (src == dst) return dst; // If no change is required, return
float ease = (dst - src) * easing;
if (abs(ease) < 1) ease /= abs(ease); // Make the value of ease at least 1
return int(src + ease);
}``````

# Tweening

I'm going to use the term "tweening" to refer to the use of easing techniques to animate shapes, to distinguish it from "easing". The principle is the same, however, points can be progressively changed from one position to another over time. The difference is that to transform a shape we must ease multiple points that define the outline of the shape. For example, the following sketch tweens between a large square (`shape1`) and a small diamond (`shape2`):

• Show Sketch
``````/** @peep sketchcode */
float[][] shape1 = {{-80, -80}, {80, -80}, {80, 80}, {-80, 80}}; // Source shape
float[][] shape2 = {{-60, 0}, {0, -20}, {60, 0}, {0, 20}}; // Destination shape
float[][] points;
float easing = 0.02; // Size of each step along the path

void setup() {
size(200, 200);
background(0);
// Make a copy of the shape1 array in points
points = new float[shape1.length];
for (int i = 0; i < shape1.length; i++) {
points[i] = shape1[i];
points[i] = shape1[i];
}
}

void draw() {
// Fade the background
noStroke();
fill(0, 12);
rect(0, 0, width, height);
// Update current points towards the target shape
for (int i = 0; i < points.length; i++) {
if (dist(points[i], points[i], shape2[i], shape2[i]) > 1.0) {
points[i] += (shape2[i] - points[i]) * easing;
points[i] += (shape2[i] - points[i]) * easing;
}
}

// Draw the current shape
noFill();
stroke(255);
translate(width/2, height/2);
beginShape();
for (int i = 0; i < points.length; i++) {
vertex(points[i], points[i]);
}
endShape(CLOSE);
}``````

By using some additional variables to record which of the shapes is the current source and destination for the tween we can easily swap the shapes used for the tween when the current points are within 1 pixel of the current destination. The `tweenCompleted` variable is used to determine whether all of the points are close enough to the destination.

• Show Sketch
``````/** @peep sketchcode */
float[][] shape1 = {{-80, -80}, {80, -80}, {80, 80}, {-80, 80}};
float[][] shape2 = {{-60, 0}, {0, -20}, {60, 0}, {0, 20}}; // Destination shape
float[][] src, dst, points;
float easing = 0.02; // Size of each step along the path

void setup() {
size(200, 200);
background(0);
// Record the initial source and destination shapes
src = shape1;
dst = shape2;
// Make a copy of the shape1 array in points
points = new float[src.length];
for (int i = 0; i < src.length; i++) {
points[i] = src[i];
points[i] = src[i];
}
}

void draw() {
// Fade the background
noStroke();
fill(0, 12);
rect(0, 0, width, height);

boolean tweenCompleted = true; // Flag to record when the tween is done
// Update current points towards the target shape
for (int i = 0; i < points.length; i++) {
if (dist(points[i], points[i], dst[i], dst[i]) > 1.0) {
points[i] += (dst[i] - points[i]) * easing;
points[i] += (dst[i] - points[i]) * easing;
// If any of the points are more than 1 pixel from target, then not done
tweenCompleted = false;
}
}
// If the tween is done, swap the source and destination shapes
if (tweenCompleted) {
float[][] tmp = src; // Make a temporary copy of the source
src = dst; // Copy the destination to the source
dst = tmp; // Copy the temporary copy to the destination
}

// Draw the current shape
noFill();
stroke(255);
translate(width/2, height/2);
beginShape();
for (int i = 0; i < points.length; i++) {
vertex(points[i], points[i]);
}
endShape(CLOSE);
}``````

The code that swaps the source and destination shapes is something that you may see in different forms quite often, it uses a temporary variable to make a copy of the value for the source before copying the value of destination variable to the source shape. Finally the temporary value is copied to the destination shape.

Instead of using two variables, `shape1` and `shape2`, to store the values of the points we can use an array to store the values of the points for these shapes. The following sketch shows how this works:

• Show Sketch
``````/** @peep sketchcode */
float[][] shapes = {
{{-80, -80}, {80, -80}, {80, 80}, {-80, 80}},
{{-60, 0}, {0, -20}, {60, 0}, {0, 20}}
};
float[][] src, dst, points;
float easing = 0.02; // Size of each step along the path

void setup() {
size(200, 200);
background(0);
// Record the initial source and destination shapes
src = shapes;
dst = shapes;
// Make a copy of the source shape in points
points = new float[src.length];
for (int i = 0; i < src.length; i++) {
points[i] = src[i];
points[i] = src[i];
}
}

void draw() {
// Fade the background
noStroke();
fill(0, 12);
rect(0, 0, width, height);

boolean tweenCompleted = true; // Flag to record when the tween is done
// Update current points towards the target shape
for (int i = 0; i < points.length; i++) {
if (dist(points[i], points[i], dst[i], dst[i]) > 1.0) {
points[i] += (dst[i] - points[i]) * easing;
points[i] += (dst[i] - points[i]) * easing;
// If any of the points are more than 1 pixel from target, then not done
tweenCompleted = false;
}
}
// If the tween is done, swap the source and destination shapes
if (tweenCompleted) {
float[][] tmp = src; // Make a temporary copy of the source
src = dst; // Copy the destination to the source
dst = tmp; // Copy the temporary copy to the destination
}

// Draw the current shape
noFill();
stroke(255);
translate(width/2, height/2);
beginShape();
for (int i = 0; i < points.length; i++) {
vertex(points[i], points[i]);
}
endShape(CLOSE);
}``````

Now that the shapes are stored in an array, we can easily add more shapes. The following sketch uses 6 different shapes. Instead of simply swapping the source and destination shapes when a tween in completed, this sketch chooses a new destination shape at random from the array.

• Show Sketch
``````/** @peep sketchcode */
float[][] shapes = {
{{-80, -80}, {80, -80}, {80, 80}, {-80, 80}},
{{-60, 0}, {0, -20}, {60, 0}, {0, 20}},
{{-40, -40}, {40, -40}, {40, 40}, {-40, 40}},
{{-80, 0}, {0, -80}, {80, 0}, {0, 80}},
{{-20, -80}, {20, -80}, {20, 80}, {-20, 80}},
{{-80, -20}, {80, -20}, {80, 20}, {-80, 20}}};
float[][] src, dst, points;
float easing = 0.02; // Size of each step along the path

void setup() {
size(200, 200);
background(0);
// Record the initial source and destination shapes
src = shapes;
dst = shapes;
// Make a copy of the source shape in points
points = new float[src.length];
for (int i = 0; i < src.length; i++) {
points[i] = src[i];
points[i] = src[i];
}
}

void draw() {
// Fade the background
noStroke();
fill(0, 12);
rect(0, 0, width, height);

boolean tweenCompleted = true; // Flag to record when the tween is done
// Update current points towards the target shape
for (int i = 0; i < points.length; i++) {
if (dist(points[i], points[i], dst[i], dst[i]) > 1.0) {
points[i] += (dst[i] - points[i]) * easing;
points[i] += (dst[i] - points[i]) * easing;
// If any of the points are more than 1 pixel from target, then not done
tweenCompleted = false;
}
}
// If the tween is done, choose a new destination
if (tweenCompleted) {
src = dst;
dst = shapes[int(random(shapes.length))];
}

// Draw the current shape
noFill();
stroke(255);
translate(width/2, height/2);
beginShape();
for (int i = 0; i < points.length; i++) {
vertex(points[i], points[i]);
}
endShape(CLOSE);
}``````

The limitation of the above sketch is that all of the shapes must contain the same number of points, but this limitation can be overcome by making doubling up points in a shape that has fewer sides.

• Show Sketch
``````/** @peep sketchcode */
float[][] shapes = {
{{-20, -80}, { 20, -80}, { 80, -80},
{ 80, -20}, { 80,  20}, { 80,  80},
{ 20,  80}, {-20,  80}, {-80,  80},
{-80,  20}, {-80, -20}, {-80, -80}},
{{-20, -80}, { 20, -80}, { 20, -20},
{ 80, -20}, { 80,  20}, { 20,  20},
{ 20,  80}, {-20,  80}, {-20,  20},
{-80,  20}, {-80, -20}, {-20, -20}},
{{-30, -80}, { 30, -80}, { 60, -60},
{ 80, -30}, { 80,  30}, { 60,  60},
{ 30,  80}, {-30,  80}, {-60,  60},
{-80,  30}, {-80, -30}, {-60, -60}}};
float[][] src, dst, points;
float easing = 0.02; // Size of each step along the path

void setup() {
size(200, 200);
background(0);
// Record the initial source and destination shapes
src = shapes;
dst = shapes;
// Make a copy of the source shape in points
points = new float[src.length];
for (int i = 0; i < src.length; i++) {
points[i] = src[i];
points[i] = src[i];
}
}

void draw() {
// Fade the background
noStroke();
fill(0, 12);
rect(0, 0, width, height);

boolean tweenCompleted = true; // Flag to record when the tween is done
// Update current points towards the target shape
for (int i = 0; i < points.length; i++) {
if (dist(points[i], points[i], dst[i], dst[i]) > 1.0) {
points[i] += (dst[i] - points[i]) * easing;
points[i] += (dst[i] - points[i]) * easing;
// If any of the points are more than 1 pixel from target, then not done
tweenCompleted = false;
}
}
// If the tween is done, choose a new destination
if (tweenCompleted) {
src = dst;
dst = shapes[int(random(shapes.length))];
}

// Draw the current shape
noFill();
stroke(255);
translate(width/2, height/2);
beginShape();
for (int i = 0; i < points.length; i++) {
vertex(points[i], points[i]);
}
endShape(CLOSE);
}``````

Use a piece of paper, or the tool below, to design a set of shapes to animate with a tween using the above sketch.

• Show Code
``````/** @peep sketch */
ArrayList points;
PVector currentPoint;

void setup() {
size(200, 200);
points = new ArrayList();
textFont(loadFont("sansserif"), 18);
textAlign(CENTER, CENTER);
}

void draw() {
background(204);
stroke(0, 32);
strokeWeight(1);
translate(width/2, height/2);
for (int x = -width/2; x <= width/2; x += 10) line(x, -height/2, x, height);
for (int y = -width/2; y <= height/2; y += 10) line(-width/2, y, width, y);
stroke(0, 64);
line(-width/2, 0, width/2, 0);
line(0, -height/2, 0, height/2);

noStroke();
fill(96, 64, 0, 128);
Iterator it = points.iterator();
while (it.hasNext()) {
PVector p = (PVector) it.next();
ellipse(p.x, p.y, 8, 8);
fill(0, 64);
}

if (points.size() > 1) {
noFill();
stroke(0, 64);
strokeWeight(4);
beginShape();
it = points.iterator();
while (it.hasNext()) {
PVector p = (PVector) it.next();
vertex(p.x, p.y);
}
endShape(CLOSE);
}

if (currentPoint != null) {
noStroke();
fill(191, 0, 0);
ellipse(currentPoint.x, currentPoint.y, 8, 8);

fill(255);
String strCoords = "(" + currentPoint.x + ", " + currentPoint.y + ")";
text(strCoords, 0, 0);
}
}

void mousePressed() {
float x = 10 * round(0.1 * (mouseX - width/2));
float y = 10 * round(0.1 * (mouseY - height/2));
currentPoint = null;
Iterator it = points.iterator();
while (it.hasNext()) {
PVector p = (PVector) it.next();
if ((p.x == x) && (p.y == y)) {
currentPoint = p;
}
}
if (currentPoint == null) {
currentPoint = new PVector(x, y);
points.add(currentPoint);
}
}

void mouseDragged() {
float x = 10 * round(0.1 * (mouseX - width/2));
float y = 10 * round(0.1 * (mouseY - height/2));
if (currentPoint != null) {
currentPoint.x = x;
currentPoint.y = y;
}
}``````

# Easing using Curves

Simple curves can provide paths for shapes in motion. Here's the curve for `y = x^2`, it uses the `pow()` function:

• Show Code
``````/** @peep sketch */
size(200, 200);
background(204);
stroke(0, 32);
for (int x = 0; x <= width; x += 10) { line(x, 0, x, height); }
for (int y = 0; y <= height; y += 10) { line(0, y, width, y); }
noFill();
stroke(0);
beginShape();
float exponent = 2.0;
for (float x = 0; x <= 1; x += 0.001) {
float y = pow(x, exponent);
vertex(x*width, y*width);
}
endShape();
popMatrix();``````

Experiment with different values for `exponent` to see how this affects the shape of the curve.

Instead of drawing the entire curve in one frame, it's possible to calculate each step of the curve on consecutive frames. The following example presents a very general way to write this code.

• Show Sketch
``````/** @peep sketchcode */
float beginX; // Initial x-coordinate
float beginY; // Initial y-coordinate
float endX; // Final x-coordinate
float endY; // Final y-coordinate
float distX; // Distance to travel in x
float distY; // Distance to travel in y
float step; // Size of each step along the path
float pct; // Percentage traveled (0.0 to 1.0)
float exponent; // Determines the curve

void setup() {
size(200, 200);
background(0);
noStroke();
beginX = 20;
beginY = 20;
endX = width - 20;
endY = height - 20;
distX = endX - beginX;
distY = endY - beginY;
step = 0.01;
pct = 0.0;
exponent = 2.0;
}

void draw() {
fill(0, 2);
rect(0, 0, width, height);
if (pct < 1.0) {
float rateX = pct;
float rateY = pow(pct, exponent);
float x = beginX + (rateX * distX);
float y = beginY + (rateY * distY);
fill(255);
ellipse(x, y, 20, 20);
pct += step;
}
}``````

If we want to use the curve to control the speed of the objects motion along a straight line this can be easily accomplished:

• Show Sketch
``````/** @peep sketchcode */
float beginX; // Initial x-coordinate
float beginY; // Initial y-coordinate
float endX; // Final x-coordinate
float endY; // Final y-coordinate
float distX; // Distance to travel in x
float distY; // Distance to travel in y
float step; // Size of each step along the path
float pct; // Percentage traveled (0.0 to 1.0)
float exponent; // Determines the curve

void setup() {
size(200, 200);
background(0);
noStroke();
beginX = 20;
beginY = 20;
endX = width - 20;
endY = height - 20;
distX = endX - beginX;
distY = endY - beginY;
step = 0.01;
pct = 0.0;
exponent = 2.0;
}

void draw() {
fill(0, 2);
rect(0, 0, width, height);
if (pct < 1.0) {
pct += step;
}
float rate = pow(pct, exponent);
float x = beginX + (rate * distX);
float y = beginY + (rate * distY);
fill(255);
ellipse(x, y, 20, 20);
}``````

Experiment with the value of `exponent` to see the different types of easing that you can create.

## Comments

Nobody has said anything yet.