Drawing Curves
How To Draw Curves
If you want to draw a circular path, it is easy enough to generate the points in the XY plane using:
import { Vector3 } from "@babylonjs/core/Maths/math.vector";const path = [];for(let theta = 0; theta < 2 * Math.PI; theta +=deltaTheta ) { path.push(new Vector3(radius * Math.cos(theta), radius * Math.sin(theta), 0));If you want to, you can work out some quite complex paths by hand.
What follows is how to draw some mathematical curves by using the Babylon.js Curve3 object, from which you can extract the array of points you need to draw lines, ribbons, tubes and extruded shapes.
The general form is
import { Curve3 } from "@babylonjs/core/Maths/math.path";const curve = Curve3.Create.CURVETYPE(parameters);Arc Through Three Points
Available from version 5.0.0. Given three points in space that are not in a straight line, it is always possible to join them with a circular arc or to draw a circle through all three points.
import { Curve3 } from "@babylonjs/core/Maths/math.path";const arc = Curve3.ArcThru3Points(first, second, third, steps, closed, fullCircle);- first Vector3 the first point the arc must pass through.
- second Vector3 the second point the arc must pass through.
- third Vector3 the third point the arc must pass through.
- steps number the larger the number of steps the more detailed the arc.
- closed boolean optional with default false, when true forms the chord from the first and third point
- fullCircle boolean optional with default false, when true forms the complete circle through the three points
This static method returns an instance of Curve3.
Just use the Curve3 getPoints() method to fill your array : getPoints() returns an array of successive Vector3.
The length() method returns the curve length.
const path = arc.getPoints();const l = arc.length();Quadratic Bezier Curve
http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Quadratic_curves

import { Curve3 } from "@babylonjs/core/Maths/math.path";const bezier2 = Curve3.CreateQuadraticBezier(origin, control, destination, nb_of_points);- origin : Vector3 the origin point,
- control : Vector3 the control point,
- destination : Vector3 the destination point,
- nb_of_points : number number of points wanted in the path array.
This static method returns an instance of Curve3.
Just use the Curve3 getPoints() method to fill your array : getPoints() returns an array of successive Vector3.
The length() method returns the curve length.
const path = bezier2.getPoints();const l = bezier2.length();Cubic Bezier curve
http://en.wikipedia.org/wiki/B%C3%A9zier_curve# Higher-order_curves

import { Curve3 } from "@babylonjs/core/Maths/math.path";const bezier3 = Curve3.CreateCubicBezier(origin, control1, control2, destination, nb_of_points);- origin : Vector3 the origin point,
- control1 : Vector3 the first control point,
- control2 : Vector3 the second control point,
- destination : Vector3 the destination point,
- nb_of_points : number the desired number of points in the final curve array.
This static method returns an instance of Curve3.
Just use the Curve3 getPoints() method to fill your array : getPoints() returns an array of successive Vector3.
The length() method returns the curve length.
const path = bezier3.getPoints();const l = bezier3.length();Hermite Spline
http://en.wikipedia.org/wiki/Cubic_Hermite_spline

import { Curve3 } from "@babylonjs/core/Maths/math.path";const hermite = Curve3.CreateHermiteSpline(p1, t1, p2, t2, nbPoints);- p1 : Vector3 the origin point,
- t1 : Vector3 the origin tangent vector,
- p2 : Vector3 the destination point,
- t2 : Vector3 the destination tangent vector,
- nbPoints : number the desired number of points in the final curve array.
This static method returns an instance of Curve3.
Just use the Curve3 getPoints() method to fill your array : getPoints() returns an array of successive Vector3.
The length() method returns the curve length.
const path = hermite.getPoints();const l = hermite.length();Catmull-Rom Spline
https://en.wikipedia.org/wiki/Cubic_Hermite_spline#Catmull.E2.80.93Rom_spline

import { Curve3 } from "@babylonjs/core/Maths/math.path";const nbPoints = 20; // the number of points between each Vector3 control pointsconst points = [vec1, vec2, ..., vecN]; // an array of Vector3 the curve must pass through : the control pointsconst closed = true; // closes the curve when trueconst catmullRom = Curve3.CreateCatmullRomSpline(points, nbPoints, closed);- points : Vector3 an array of Vector3 (the control points) the curve will pass through,
- nbPoints : number the wanted curve number of points between each control point,
- closed : boolean optional with default false; available from BJS V3.3; when true forms a closed curve.
This static method returns an instance of Curve3.
Just use the Curve3 getPoints() method to fill your array : getPoints() returns an array of successive Vector3.
The length() method returns the curve length.
const path = catmullRom.getPoints();const l = catmullRom.length();Hermite Quaternion Spline
import { Quaternion } from "@babylonjs/core/Maths/math.vector";Quaternion.Hermite(point0, tangent0, point1, tangent1, amount);This allows the interpolation of quaternions for use in animation. As a quaternion is a 4D object, how can you visualize the Hermite quaternion spline used by the interpolation? By representing the hyperspline in 3D space.
This is achieved by mapping a rotation quaternion onto a 3D vector.
The Math
What is needed is a function from rotation quaternions to 3D vectors of the form
For a rotation quaternion there are two things to note:
- and are equivalent.
The equivalence of and can be seen in the following
Equivalent Rotation QuaternionsIt follows that the set of rotation quaternions where and and covers all possible rotations.
and so
- Eq 1
Since then
Dividing Eq 1 by , which can never be
= =
This gives the mapping
= (, , )
Each rotation quaternion where is mapped onto one of a series of concentric spherical shells of radius, ,
= .

The center of the shells represents the rotation quaternion ; the outer white shell is a unit sphere where .
The process can be reversed: the inverse function returns the rotation quaternion from a point on the shells.
Since the radius, r, of the sphere at is such that and
= then
= and
= and
= (, , , )
Drawing
The following function will return a Curve3 within a unit sphere representing a Hermite quaternion spline in 3D space using the math above.
import { Curve3 } from "@babylonjs/core/Maths/math.path";import { Quaternion, Vector3 } from "@babylonjs/core/Maths/math.vector";const hermiteQuarternionSpline = (p1, t1, p2, t2, nbPoints) => { const hermite = new Array(); const step = 1.0 / nbPoints; for (let i = 0; i <= nbPoints; i++) { const q = Quaternion.Hermite(p1, t1, p2, t2, i * step); q.normalize(); if (q.w < 0) { q.scaleInPlace(-1); } const v = new Vector3(q.x / (1 + q.w), q.y / (1 + q.w), q.z / (1 + q.w)); hermite.push(v); } return new Curve3(hermite);};- p1 : Quaternion the origin point,
- t1 : Quaternion the origin tangent vector,
- p2 : Quaternion the destination point,
- t2 : Quaternion the destination tangent vector,
- nbPoints : number the wanted final curve number of points in the array.
Warning
Using BABYLON.Quaternion.RotationAxis(axis, angle) to create any of p1, t1, p2, or t2 does not produce the expected results. Other ways of producing rotation quaternions, apart from creating them directly, should also be checked to ensure the resulting quaternion is in the range required for the mapping to work.
To produce a vector on the outer shell, you need a rotation quaternion with .
Take the rotation quaternion from
import { Quaternion } from "@babylonjs/core/Maths/math.vector";new Quaternion(1, 1, 1, 0).normalize(); //giving (0.5774, 0.5774, 0.5774, 0) to 4 dpHowever using
import { Quaternion, Vector3 } from "@babylonjs/core/Maths/math.vector";Quaternion.RotationAxis(new Vector3(1, 1, 1), 0); // gives (0, 0, 0, 1)and while this may be an equivalent quaternion, it places the vector at the center of the shells, not on the outer shell.
End Warning
As it is difficult to visualize the spline from pure quaternions it would be useful if there was an editor to draw the representation of the spline in 3D space.
A (Very) Basic Editor
Hermite Quaternion EditorThis playground allows you to drag representations of the start (green) and end (red) quaternions within the unit sphere in 3D space. The representations of the start and end quaternion tangents (purple) are attached to the respective start and end controls. The tangents can also be dragged around the invisible sphere shells centered on the start and end controls. The shells, and hence the radius, can be adjusted using the up and down arrow keys (or w and x) for the selected control. While dragging or adjusting the radius, the camera is detached. The camera is reattached whenever dragging ends, and when using keys you can reattach it by pressing the spacebar.
The representation of the spline in 3D space is drawn as you adjust the controls. To apply the created Hermite quaternion spline to the box, click the animate button.
Custom Curve3 Object
You can also make your own Curve3 object from a simple array of successive Vector3.
Why would you do this and not just use the points to draw a line?
Because the Curve3 object has a useful method, continue(), that allows you to place the start of one Curve3 at the end of another Curve3 without any calculations to match the start and end points of the curves.
Example 1
Create an array of Vector3 along a simple sinus curve.
import { Curve3 } from "@babylonjs/core/Maths/math.path";import { Vector3 } from "@babylonjs/core/Maths/math.vector";const mySinus = [];for (let i = 0; i < 30; i++) { mySinus.push(new Vector3(i, Math.sin(i / 10), 0));}const mySinusCurve = new Curve3(mySinus);You would like to continue your mySinus curve with a bezier3 curve and then join on a bezier2.
const myFullCurve = mySinusCurve.continue(bezier3).continue(bezier2);The continue() method returns a new Curve3 object and leaves mySinusCurve3, bezier3 and bezier2 unchanged.
If you then need to draw the curve or use it for anything else, just get the array of points with the getPoints() method. This method simply returns an array of successive Vector3.
import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder";const path = myFullCurve.getPoints();const extruded = MeshBuilder.ExtrudeShape("extrudedShape", { shape: shape, path: path, scale: 1, rotation: 0 }, scene);If you need then to know the curve length, just use the length() method.
const l = myFullCurve.length();Example 2
Here is an example where a Hermite spline is used to smoothly close a concatenation of two Bezier curves. As the spline closes the curves, the first and last points of the open continued curve need to be read from the array.
- The first and last points of the concatenation are used as last and first point of the Hermite spline.
- The first and last segments of the concatenation are used as the last and first tangent vectors of the Hermite. Since these segments are quite small, they are scaled according to the concatenation length, so the longer the concatenation, the more curved the spline.
import { Curve3 } from "@babylonjs/core/Maths/math.path";import { MeshBuilder } from "@babylonjs/core/Meshes/meshBuilder";// two concatened cubic Bezierconst cubicA = Curve3.CreateCubicBezier(vA0, vA1, vA2, vA3, 50);const cubicB = Curve3.CreateCubicBezier(vB0, vB1, vB2, vB3, 50);const continued = cubicA.continue(cubicB);// initial Hermite values from continued first and last segmentsconst t = continued.length() / 2; // tangent scale factorconst points = continued.getPoints();const p1 = points[points.length - 1]; // last continued point = first hermite pointconst t1 = p1.subtract(points[points.length - 2]).scale(t); // last segment scaled = hermite tangent t1const p2 = points[0]; // first continued point = last hermite pointconst t2 = points[1].subtract(p2).scale(t); // first segment scaled = hermite tangent t2const hermite = Curve3.CreateHermiteSpline(p1, t1, p2, t2, 50);continued = continued.continue(hermite);// finally drawing a smooth closed curveconst closedCurve = MeshBuilder.CreateLines("closed", { points: continued.getPoints() }, scene);The orange and yellow curves are the original Bezier curves.
In light blue, these two curves are connected to each other, and a Hermite curve is also added to close the path.
Further reading
Creating Parametric Meshes
Learn how to create parametric meshes in Babylon.js.

Path3D
Learn how to use Path3D in Babylon.js.
