Skip to content
Engine

Math and Space

Spline

Smooth 3D paths through control points. Sample positions and orientations, move at constant speed, find the nearest point and build tubes.

PackageShared

Getting it

local Spline = Engine:GetPackage("Spline")

Overview

A spline is a smooth curve defined by a few control points. Use it for camera paths, rails, roller coasters, rivers, NPC patrol routes or anything that should move along a curve.

Every spline is parameterised by t from 0 (start) to 1 (end); values outside that range are clamped. Because t does not grow evenly with distance on a curve, use GetPointByDistance or GetLength when you need constant speed.

A part that flies along a path at constant speed
local Spline = Engine:GetPackage("Spline")

local path = Spline.Catmull({
	Vector3.new(0, 10, 0),
	Vector3.new(40, 20, 10),
	Vector3.new(80, 15, -20),
	Vector3.new(120, 30, 0),
})

local length = path:GetLength()
local speed = 25 -- studs per second
local travelled = 0

RunService.Heartbeat:Connect(function(dt)
	travelled = (travelled + speed * dt) % length
	local t = path:Project(path:GetPointByDistance(travelled))
	part.CFrame = path:GetCFrame(t)
end)

Kinds of spline

ConstructorPasses through points?Notes
Spline.Catmull(points, { loop?, tension? })YesThe usual choice. tension 0 is the classic smooth curve; higher values tighten corners. Needs 2 points (3 for a loop).
Spline.Linear(points, { loop? })YesStraight segments.
Spline.Hermite(points, tangents, { loop? })YesYou give the direction and strength at each point.
Spline.BSpline(points, { loop? })NoVery smooth; points act as magnets.
Spline.Bezier(points)Only the endsOne Bézier curve through all control points.

With loop = true the last point connects back to the first.

Sampling

MethodDescription
:GetPoint(t): Vector3Position.
:GetTangent(t): Vector3Direction of travel (not normalised).
:GetAcceleration(t): Vector3Second derivative.
:GetCurvature(t): numberHow sharply the path bends (1 / radius).
:GetNormal(t): Vector3Direction the path bends towards.
:GetFrenetFrame(t): (tangent, normal, binormal)Orientation frame from the curve itself.
:GetCFrame(t): CFramePosition looking along the path, upright by the configured up vector.
:Sample(n): { CFrame }n CFrames spread evenly along the length.
:ToPoints(n): { Vector3 }n positions.

Distance

MethodDescription
:GetLength(t0?, t1?): numberLength of the whole spline, or between two t values.
:GetPointByDistance(distance): Vector3Position a given number of studs from the start.
:Project(position): numberThe t of the point on the spline closest to position. Great for "how far along the track is this player".

Lengths come from a lookup table built the first time you ask; its accuracy is the DefaultLengthSamples setting.

Shape

MethodDescription
:GetBounds(): (min, max)Bounding box corners.
:GetCenter(): Vector3Centre of the bounds.
:Reverse(): SplineThe same path backwards.
:Offset(distance): SplineA parallel path distance studs to the side, e.g. for the second rail of a track.
:SubSpline(t0, t1): SplinePart of the path as its own spline.
:GetCrossSection(t, radius, sides?): { Vector3 }A ring of points around the path at t.
:GetCrossSections(n, radius, sides?): { { Vector3 } }n rings along the path, for building your own tube mesh. To sweep any 2D shape along a spline, use GeometryService:ExtrudeAsync.

Inspection

:GetKind(), :GetControlPoints(), :GetTangents() (Hermite only), :GetTension() (Catmull only), :GetSegmentCount() and :IsLoop().

Configuration

Defaults from DefaultConfiguration/Packages/Spline6 settings
SettingDefaultWhat it does
DefaultTension0Tension Spline.Catmull() uses when config.tension is omitted.
DefaultLoopfalseWhether a spline closes back on itself when config.loop is omitted.
DefaultUpVector3.yAxisUp-vector hint :GetCFrame() uses when none is passed.
DefaultLengthSamples64Samples in the lazily built arc-length table; higher is more accurate but costs more.
DefaultProjectRefinementIterations24Golden-section steps :Project() refines past its nearest table sample.
DefaultCrossSectionSides8Sides :GetCrossSection(s)() generate when none is passed.

Types

export type Spline
export type SplineCatmullConfig = { loop: boolean?, tension: number? }
export type SplineLoopConfig = { loop: boolean? }