@web-engine-dev/splines
Comprehensive spline and curve library providing 8 curve types, path utilities, and mesh deformation for the web-engine-dev ecosystem. Used throughout the engine for camera rails, animation curves, terrain paths, procedural geometry, and cinematic sequencing.
Layer 1 · Standalone (no ECS dependency)
Features
- 8 Curve Types: Bezier, Catmull-Rom, B-Spline, Hermite, NURBS, Linear, Cardinal, Kochanek-Bartels (TCB)
- Arc-Length Parameterization: Constant-speed traversal via Gauss-Legendre quadrature lookup tables
- SplinePath: Composite paths from multiple curve segments with automatic knot joining
- SplineFollower: Entities follow paths with configurable speed, looping, and ping-pong modes
- SplineMeshDeformer: Bend meshes along spline paths (roads, rivers, pipes)
- SplineProjector: Nearest-point queries for distance calculations
- 2D & 3D Support: All utilities work with both 2D and 3D splines
- Adaptive Sampling: Curvature-based sampling places more points in tight curves
Installation
bash
npm install @web-engine-dev/splines
# or
pnpm add @web-engine-dev/splinesQuick Start
typescript
import { CatmullRomCurve, SplinePath, SplineFollower } from '@web-engine-dev/splines';
import { Vec3 } from '@web-engine-dev/math';
// Create a Catmull-Rom curve through control points
const curve = new CatmullRomCurve([
new Vec3(0, 0, 0),
new Vec3(5, 3, 0),
new Vec3(10, 0, 5),
new Vec3(15, 2, 0),
]);
// Evaluate position and tangent at parameter t
const position = curve.evaluate(0.5);
const tangent = curve.tangent(0.5);
// Get arc length for constant-speed traversal
const totalLength = curve.arcLength();
// Build a composite path from multiple segments
const path = new SplinePath();
path.addSegment(curve);
// Follow a path at constant speed
const follower = new SplineFollower(path, {
speed: 5.0,
loop: true,
mode: 'pingPong',
});Curve Types
| Curve Type | Continuity | Best For | Key Property |
|---|---|---|---|
| Bezier | C0 (cubic: C1) | Artist-controlled paths, UI curves | Intuitive control point manipulation |
| Catmull-Rom | C1 | Camera rails, waypoint paths | Passes through all control points |
| B-Spline | C2 | Smooth surfaces, animation curves | Approximating — never overshoots |
| Hermite | C1 | Physics trajectories, controlled arcs | Explicit tangent control at endpoints |
| NURBS | C2 | CAD-quality curves, precise geometry | Weighted control — exact circles/conics |
| Cardinal | C1 | Tension-tuned paths | Single tension parameter (0 = Catmull-Rom) |
| Kochanek-Bartels | C1 | Animation with overshoot/anticipation | Tension + Continuity + Bias per point |
| Linear | C0 | Debug visualization, grid paths | Cheapest — no computation needed |
Unified Interface
All 8 curve types implement the same SplineCurve interface:
typescript
interface SplineCurve {
evaluate(t: number): Vec3; // Position at parameter t
tangent(t: number): Vec3; // Tangent direction at t
arcLength(): number; // Total arc length
closestPoint(p: Vec3): number; // Nearest t to point p
}This means SplineFollower, SplineMeshDeformer, and SplineProjector work identically regardless of curve type. Swapping from Catmull-Rom to B-Spline requires zero consumer code changes.
Arc-Length Parameterization
Raw spline parameters (t = 0 to 1) produce non-uniform speed. Arc-length parameterization builds a lookup table using Gauss-Legendre quadrature that maps uniform distance to the correct t parameter:
typescript
// Without arc-length: entity moves faster through less-curved segments
const raw = curve.evaluate(t);
// With arc-length: constant-speed traversal
const sampler = new SplineSampler(curve);
const uniform = sampler.evaluateAtDistance(distance);ECS Integration
typescript
import { SplineComponent, SplineFollowerComponent } from '@web-engine-dev/splines';
// Attach a spline to an entity
world.insert(entity, SplineComponent, {
curve: myCatmullRomCurve,
});
// Make an entity follow a spline path
world.insert(entity, SplineFollowerComponent, {
speed: 5.0,
loop: true,
});Dependencies
@web-engine-dev/math— Vector and matrix types