# Working with paths

## **Path Time**

A path is a continuous curve made up of anchors. We can use `path.anchors` to get information about the path at the anchor points, but what about in-between the anchors?

That’s where path “time” comes in. For an open path, time is a number that goes from `0` to `anchors.length - 1`. On closed paths, time can go up to `anchors.length` (completing the loop). You can think of time as an animation from the start of the path when time is 0, to the end.

**Note:** Time is an integer at anchors, so `path.positionAtTime(1)` will return the position of the path’s second anchor.

Let’s use path time to get a continuous position along the path:

// For each path in the input... input.allPaths().forEach((path) => { console.log(path.anchors.length); const position = path.positionAtTime(0.0); console.geometry(Anchor(position)); }); return input;

![](https://content.cuttle.xyz/learn/working-with-paths/8d0369cca2befd6e0ad1544bece7d9587cc1f4b0d4fafe0412f32cb327c80a9f.gif)

## **Example: Repeating a shape along a Path**

Now that we can get the position on a path at any time, let’s use it to place some shapes.

// Create a shape. const triangle = Polygon({ sides: 3 }).transform({ scale: 0.25, }); // Give our shape a style. triangle.copyStyle(input); // Accumulate some output. const output = \[\]; input.allPaths().forEach((path) => { // Step time at a constant rate. range(0, path.anchors.length - 1, 0.25).forEach((time) => { // Get a position from the path. const position = path.positionAtTime(time); // Push a triangle at that position to the output. output.push(triangle.clone().transform({ position })); }); }); return output;

![](https://cuttle-content.imgix.net/learn/working-with-paths/0802e0a435c430461bcafbacd99f157994ff4bc8fcec0cf50e7785f16f330baa.png?auto=compress,format&q=60&fit=max&w=720&h=720&dpr=2)

## **Time at Distance**

You might have noticed that our shapes are not evenly spaced in the previous example. That’s because Bezier paths are non-linear, meaning the “position” on the path might speed up or slow down depending on the anchor handles.

![](https://content.cuttle.xyz/learn/working-with-paths/8721d84b2ce16167affc8a3cfb257fac46d25c51a8f5cabe7ded2ae86e483556.gif)

If we want our shapes spaced evenly, we need to step along the path by “distance” rather than time. We can use the `path.timeAtDistance()` to turn a distance into a time, and then use the time to get a position.

## **Example: Repeating along a Path (Distance)**

Instead of stepping along the path at a fixed time interval, we’ll step at a fixed distance up to the length of the path.

// Create a shape. const triangle = Polygon({ sides: 3 }).transform({ scale: 0.25, }); // Give our shape a style. triangle.copyStyle(input); // Accumulate some output. const output = \[\]; input.allPaths().forEach((path) => { // Get the total arc length of the path. const length = path.length(); // Step distance at a constant rate. range(0, length, 0.25).forEach((distance) => { // Convert distance to time. const time = path.timeAtDistance(distance); // Get a position from the path. const position = path.positionAtTime(time); // Push a triangle at that position to the output. output.push(triangle.clone().transform({ position })); }); }); return output;

![](https://cuttle-content.imgix.net/learn/working-with-paths/f24e7abbc1ec09c552860777ec8c4a4a6744ed1c2a3b1fceb2f87403a72bc1b4.png?auto=compress,format&q=60&fit=max&w=720&h=720&dpr=2)

Now our triangles are spaced more evenly! You can see that as the length of the path changes, the number of triangles also changes.

![](https://content.cuttle.xyz/learn/working-with-paths/f05988b83079a06ac0e99150fdebd7361929b5de2cc21463a73f487e402171ee.gif)

## **Tangents and Normals**

Now we can place shapes at positions along a path, but what if we want to orient the shapes to the face in the direction that the path is heading?

For this, we can use the path “tangent”. A tangent is like an anchor handle. It’s a unit vector that faces in the direction of the path, and we can get one for any time along the path.

Let’s write a simple modifier that logs tangents at fixed distances along the path.

// For each path in the input... input.allPaths().forEach((path) => { // Get the total arc length of the path. const length = path.length(); // Step distance at a constant rate. range(0, length, 0.25).forEach((distance) => { // Convert distance to time. const time = path.timeAtDistance(distance); // Get a position from the path. const position = path.positionAtTime(time); // Get a tangent from the path. const tangent = path.tangentAtTime(time); // Show the tangent. console.geometry(LineSegment(position, position + tangent)); }); }); return input;

![](https://cuttle-content.imgix.net/learn/working-with-paths/e055950bf9509b90649e51fdf8a516ddd246c65ed81d000f84c5a3976fa16319.png?auto=compress,format&q=60&fit=max&w=720&h=720&dpr=2)

We can show normals, too. A path “normal” is like a tangent, but oriented perpendicular to the path. A normal vector is actually just a tangent vector rotated by 90°.

// For each path in the input... input.allPaths().forEach((path) => { // Get the total arc length of the path. const length = path.length(); // Step distance at a constant rate. range(0, length, 0.25).forEach((distance) => { // Convert distance to time. const time = path.timeAtDistance(distance); // Get a position from the path. const position = path.positionAtTime(time); // Get a normal from the path. const normal = path.normalAtTime(time); // Show the normal. console.geometry(LineSegment(position, position + normal \* 0.5)); }); }); return input;

![](https://cuttle-content.imgix.net/learn/working-with-paths/70c31db3c5cc1d3f9187dff40b56f932470ab1d1b89c0c1c4dfaf32a109d8b3c.png?auto=compress,format&q=60&fit=max&w=720&h=720&dpr=2)

## **Example: Putting it all together!**

We can use `tangent.angle()` to rotate our triangles to face in the direction of the path.

const spacing = 0.3; // A small, rightwards-facing triangle. const triangle = Polygon({ sides: 3, }) .transform({ rotation: 90, scale: Vec(0.1, 0.2), }) .copyStyle(input); // We'll accumulate some output. const output = \[\]; // For each path in the input... input.allPaths().forEach((path) => { // Get the total arc length of the path. const length = path.length(); // Step along the length of the path by \`spacing\`. range(0, length, spacing).forEach((distance) => { // Convert distance to time. const time = path.timeAtDistance(distance); // Get a position from the path. const position = path.positionAtTime(time); // We can also get the tangent direction (a unit-lengthed Vec). const tangent = path.tangentAtTime(time); // Taking the angle of the tangent Vec. const angle = tangent.angle(); // Push a triangle at that position and angle to the output. output.push( triangle.clone().transform({ position, rotation: angle, }) ); }); }); return output;

![](https://cuttle-content.imgix.net/learn/working-with-paths/f456e8f5c92c48a33b23450911852e12d48f51c9c2bacce0b4c3ae290ae437c1.png?auto=compress,format&q=60&fit=max&w=720&h=720&dpr=2)