The pack
geometry3d
function geometry3d(): Pack<{ geometry3d: Geometry3dApi }>
The geometry-3d pack: pure vertex builders for every 3D primitive, and a registry so packs can add their own. Headless — it touches no GPU.
Helpers
alongPoints
function alongPoints(values: ReadonlyArray<number>, at: number, closed = false): number
A value given at each control point (a radius, a width), read at a sample's at: smoothly between the points.
arcLengths
function arcLengths(samples: ReadonlyArray<CurveSample>, closed = false): number[]
How far along a sampled curve each sample is, 0 at the start and 1 at the end (by length).
capsuleVerts
function capsuleVerts(segs = 24, rings = 8, r = 0.25): Float32Array<ArrayBuffer>
Capsule (pill): total height 1, radius r (default 0.25) — scale h for longer pills.
circleVerts
function circleVerts(segs = 32, arc = 1): Float32Array<ArrayBuffer>
Flat disc (or pie slice via arc 0..1) in the XZ plane, double-sided, unit diameter.
coatSoup
function coatSoup(target: Soup, o: CoatOptions): Soup
The coat over target: its outer surface and the skirt round its edge.
coneVerts
function coneVerts(segs = 32, open = false, arc = 1): Float32Array<ArrayBuffer>
Capped cone: unit base diameter, unit height, apex up. Smooth sides.
open skips the base and double-sides the shell (a visible inside);
a based arc (0..1) portion is sealed with flat cut walls.
cubeVerts
function cubeVerts(segs = 1): Float32Array<ArrayBuffer>
Unit cube (flat normals) — the workhorse box. segs grids each face
(visually identical when undeformed; matters under vertex fog/deformers).
cylinderVerts
function cylinderVerts(segs = 32, rTop = 1, rBottom = 1, open = false, arc = 1): Float32Array<ArrayBuffer>
Capped cylinder, diameter 1, height 1: smooth side, flat caps. Generalised
to a frustum: rTop/rBottom are each end's radius as a fraction of the
unit radius (1 = full 0.5; rTop 0 = a cone). open skips the caps and
makes the shell DOUBLE-SIDED (an open pipe has a visible inside); a capped
arc (0..1) portion is SEALED with flat cut walls (a solid wedge).
deforms
const deforms = (d: Deform | undefined): boolean=> …
Does this deform do anything?
deformSoup
function deformSoup(s: Soup, d: Deform): Soup
A reshaped copy of s (positions only; normals are left as they were — recompute them).
DIE_SIDES
const DIE_SIDES: readonly [4, 6, 8, 10, 12, 20]
The sides a die can have.
dieSoup
function dieSoup(sides: DieSides, o: DieOptions = {}): Soup
A die resting on a face, centered, fitted to its natural box (its longest side 1). Faces are numbered so opposite faces add up to sides + 1 (the d4, which has none opposite, 1–4); the top shows the highest number. Flat normals.
discVerts
function discVerts(fillet = 0.35, segs = 40, filletSegs = 6): Float32Array<ArrayBuffer>
Disc: a coin / casino chip / wheel — a cylinder whose rim edges are
FILLETED (rounded where the flat faces meet the flat side). Built as a
lathe: the 2D profile (face → quarter-round fillet → straight rim →
fillet → face) revolved around Y, with exact normals from the profile.
Unit diameter × unit height: scale h thin for a chip, roll it upright
(pitch/roll ±90°) for a wheel. fillet = fraction of the smaller
half-extent (0..1).
earClip
function earClip(pts: Array<[number, number]>): Array<[number, number, number]>
Ear-clipping triangulation of a simple polygon (no holes, no
self-intersections). Returns index triples into pts. O(n²) — geometry
builds once per bucket, so simplicity beats asymptotics here.
extrudeVerts
function extrudeVerts(outline: Array<[number, number]>, bevel = 0, bevelSegs = 3): Float32Array<ArrayBuffer>
Extrusion of a simple 2D outline ([x, z] points, auto-fit) through unit
height, with an optional rounded bevel where the walls meet the caps.
bevel = bevel size as a fraction of the half-height (0..0.9); the caps
inset by the bevel (spiky concave outlines want a SMALL bevel — the inset
is a plain miter offset, not a full polygon offset).
flatNormals
function flatNormals(s: Soup): Soup
Per-face normals: the faceted look, one flat colour a face.
flipSoup
function flipSoup(s: Soup): Soup
Every triangle wound the other way (and its normals turned round): an inside-out soup put right.
GEOMETRY_STRIDE
const GEOMETRY_STRIDE: 8
Floats per vertex in every geometry: position xyz, normal xyz and uv, so 8.
gradientNoise
function gradientNoise(seed: number): (x: number, y: number, z: number) => number
Smooth 3D gradient noise, about −1…1, the same for the same seed: lumps on a potato, bumps on a rock. procgen's Perlin noise.
gridSamples
function gridSamples(target: Soup, rows: number, columns: number, o: { region?: SurfaceRegion | undefined; stagger?: boolean | undefined; jitter?: number | undefined; seed?: number | undefined } = {}): SurfacePoint[]
Rows × columns over a surface by its uv; alternate rows half a step round (stagger, default on), like corn.
intersectVerts
function intersectVerts(a: Float32Array, b: Float32Array): Float32Array
Returns the volume common to a and b, as one triangle soup.
latheForm
function latheForm(profile: ReadonlyArray<ProfilePoint>, o: FormDetail & { arc?: number } = {}): Soup
A LATHE at real size: profile points [radius, y] (bottom to top, in the caller's units) as a smooth curve
revolved about y. An end that does not reach the axis is sealed with a flat cap.
latheVerts
function latheVerts(points: Array<[number, number]>, segs = 32, arc = 1): Float32Array<ArrayBuffer>
Lathe: a 2D profile of [d, y] points (d = distance from the axis,
authored BOTTOM → TOP) revolved around Y. Auto-fit to the unit box;
smooth normals from the profile tangents. arc sweeps a portion.
A profile that doesn't reach the axis (end d > 0) is AUTO-SEALED with a
flat cap to the axis at both ends — no pinholes at the poles.
leafForm
function leafForm(length: number, width: number, o: LeafOptions = {}): Soup
A LEAF at real size: a thin blade length long (along +y, base at the origin) and width wide (across x), its
face toward +z, closed (top and underside), folded, curled and twisted as asked.
linePoints
function linePoints(count: number, a: V3, b: V3): V3[]
count points evenly along a line from a to b.
mergeSoups
function mergeSoups(list: ReadonlyArray<Soup>): Soup
Several soups as one.
MESHES
const MESHES: Record<string, MeshBuilder>
Every built-in primitive, unit-sized. Options are per shape; anything omitted takes the shape's default.
moveSoup
function moveSoup(s: Soup, d: V3): Soup
Move a soup.
outward
function outward(s: Soup): Soup
A closed soup wound outward (CCW seen from outside, as the renderer culls).
panelVerts
function panelVerts(r = 0.35, seg = 6): Float32Array<ArrayBuffer>
Panel: an extruded ROUNDED-CORNER rectangle (unit footprint in XZ, unit
thickness in y — scale h thin for cards/plaques). r = corner radius as a
fraction of the half-extent. Smooth wall normals around the corner arcs
via the same clamp-to-core trick as the rounded box.
placeCopies
function placeCopies(item: Soup, places: ReadonlyArray<Placement>): Soup
Copies of item (built about its own origin, +y up) at each placement, merged into one soup.
planeVerts
function planeVerts(segs = 1): Float32Array<ArrayBuffer>
A double-sided unit sheet in the XZ plane, centred on the origin, facing +y on top and -y underneath: ground sheets,
water, cards laid flat. segs (rounded, at least 1; default 1) divides each side into that many cells, which a
deformer or vertex effect needs: a two-triangle plane interpolates everything across one long diagonal.
polyhedronVerts
function polyhedronVerts(kind: 'tetrahedron' | 'octahedron' | 'icosahedron' | 'dodecahedron', detail = 0): Float32Array<ArrayBuffer>
A unit-sphere-fit Platonic solid with flat faces. detail subdivides each
face 0..4 times toward a sphere. Default 0 (the raw solid).
quadVerts
function quadVerts(segs = 1): Float32Array<ArrayBuffer>
A unit quad in the XY plane facing +z. The billboard mesh: local x is right, local y is up.
ringPlacements
function ringPlacements(count: number, o: { radius?: number | undefined; tilt?: number | undefined; jitter?: number | undefined; seed?: number | undefined; scale?: [number, number] | undefined } = {}): Placement[]
Copies round a vertical axis: bases on a circle of radius, each leaning out by tilt degrees, evenly spaced.
ringVerts
function ringVerts(inner = 0.5, segs = 32, arc = 1): Float32Array<ArrayBuffer>
Flat ring/annulus (or portion via arc) in the XZ plane, double-sided.
inner = inner radius as a fraction of the outer (0..1).
roundedBoxVerts
function roundedBoxVerts(r = 0.2, seg = 7): Float32Array<ArrayBuffer>
Rounded ("chamfered") unit box: edges and corners bevel smoothly with
radius r (fraction of the half-extent, 0..0.5). The classic trick: build
each face as a grid, clamp each point to the shrunken core box, and push it
out along the (smooth) normal from the core — flat mid-faces, curved edges.
roundedPlaneVerts
function roundedPlaneVerts(r = 0.3, segs = 28): Float32Array<ArrayBuffer>
A double-sided flat sheet in the XZ plane with ROUNDED CORNERS, gridded so
it still bends — a playing card, a token, a rounded sign. r is the corner
radius as a fraction of the half-extent (0 = square, 1 = a stadium).
Why not panelVerts for a card: a panel is an EXTRUSION, so it has side
walls (a surface effect runs on them, and real cards are far too thin for
the thickness to read) and its faces are triangle FANS, which cannot carry
a vertex bend. This is a grid, exactly like planeVerts, with the outline
pushed onto a rounded rectangle.
segs sets BOTH the bend smoothness and how finely the corner arcs are
sampled, so a large on-screen card wants more of it.
scaleSoup
function scaleSoup(s: Soup, k: V3): Soup
Scale a soup about the origin (per axis), normals carried properly.
shapeVerts
function shapeVerts(outline: Array<[number, number]>): Float32Array<ArrayBuffer>
Flat filled shape: a simple 2D outline of [x, z] points triangulated (ear clipping — no holes), double-sided in the XZ plane, auto-fit.
sliceByPlane
function sliceByPlane(s: Soup, axis: 'x' | 'y' | 'z', d: number, keep: 'below' | 'above' = 'below'): { kept: Soup; face: Soup }
The soup on one side of the plane axis = d, and the cap closing it (its normal pointing out of the kept side).
smoothCurve
function smoothCurve(points: ReadonlyArray<ReadonlyArray<number>>, o: CurveOptions = {}): CurveSample[]
The curve through points, sampled. Every control point is on the curve; corners and the two ends of an open
curve are always samples. With smooth: false the samples are the points themselves.
smoothNormals
function smoothNormals(s: Soup, crease = 50): Soup
Smooth normals: every corner takes the area-weighted average of the faces meeting at its POINT whose normals are
within crease degrees of its own face's, so curved surfaces read round and real edges (a cap's rim, a corner)
stay sharp. Points are matched by position, so seams and separately built pieces join smoothly.
soupBounds
function soupBounds(s: Soup): SoupBounds
The box a soup fills; a zero box at the origin for an empty one.
soupVolume
function soupVolume(s: Soup): number
The signed volume a closed soup encloses: negative when it is wound inside out.
sphereVerts
function sphereVerts(segs = 28, rings = 18): Float32Array<ArrayBuffer>
UV sphere, diameter 1, smooth normals, equirect UVs (planet maps wrap).
spiralSamples
function spiralSamples(target: Soup, count: number, o: { region?: SurfaceRegion | undefined; jitter?: number | undefined; seed?: number | undefined } = {}): SurfacePoint[]
count points on the golden-angle spiral over a surface by its uv: the phyllotaxis of a pineapple or a pinecone.
subdivideSoup
function subdivideSoup(s: Soup, maxEdge: number, passes = 8): Soup
Split every edge longer than maxEdge at its middle, again until none is, so a shape with long flat faces (a box,
a cylinder's sides) can bend. A split depends on the edge alone, so the two triangles that share an edge split it
alike and no cracks open. passes caps the rounds.
subtractVerts
function subtractVerts(a: Float32Array, b: Float32Array): Float32Array
Returns a with b cut away, as one triangle soup.
surfaceSamples
function surfaceSamples(target: Soup, count: number, o: { region?: SurfaceRegion | undefined; seed?: number | undefined } = {}): SurfacePoint[]
count points spread EVENLY over a surface (best-candidate sampling: each new point is the farthest of a dozen
random tries from the ones already placed), within a region. The same seed gives the same points.
sweepForm
function sweepForm(path: ReadonlyArray<ReadonlyArray<number>>, o: SweepOptions): Soup
A SWEEP at real size: a section swept along a smooth path through path's control points, its radius eased from
point to point, with round, flat or pointed ends. uv: u round the section, v along the path (0…1), so a pattern
can follow the curve.
torusKnotVerts
function torusKnotVerts(p = 2, q = 3, segs = 96, sides = 10, tube = 0.24): Float32Array<ArrayBuffer>
Torus knot (p, q winding) as a closed tube, fit to the unit box.
torusVerts
function torusVerts(major = 0.34, minor = 0.16, segs = 36, sides = 18, arc = 1): Float32Array<ArrayBuffer>
Torus in the XZ plane, outer diameter 1, smooth normals. arc (0..1)
sweeps a portion of the ring; partial sweeps are SEALED with flat caps at
both cut ends (a cut sausage, not a hollow shell).
toUnitBox
function toUnitBox(s: Soup, b: SoupBounds = soupBounds(s)): Soup
Fit a soup INTO THE UNIT BOX the renderer scales by a part's size: positions centred and divided by the soup's own
extent on each axis (so it fills −0.5…0.5; pass b to fit several soups by one box), normals multiplied by that extent (the inverse transpose of the divide),
so that once the renderer scales by the size — it divides normals by the instance size — they are the normals of
the shape at that size. An axis the soup is flat along keeps a sliver of thickness, so normals never collapse.
transformVerts
function transformVerts(verts: Float32Array, xf: Transform): Float32Array
Return a copy of verts with a placement baked in: scale → rotate → move.
Normals rotate too (divided by scale first for non-uniform, then renormalised).
tubeVerts
function tubeVerts(path: number[][], r = 0.16, sides = 12, closed = false): Float32Array<ArrayBuffer>
Tube swept along a 3D polyline (auto-fit to the unit box). r = tube
radius as a fraction of the unit half-extent; closed joins the ends.
unionVerts
function unionVerts(a: Float32Array, b: Float32Array): Float32Array
Returns the union of two triangle soups as one soup — a plus b.
uvPoint
function uvPoint(target: Soup, u: number, v: number): SurfacePoint | null
The surface point at a uv (a lathe, a cylinder or a sweep's u round and v along), or null if no triangle has it.
vesselForm
function vesselForm(profile: ReadonlyArray<ProfilePoint>, wall: number, o: FormDetail & { fill?: VesselFill } = {}): VesselForm
A VESSEL at real size: a hollow lathe. profile is the OUTSIDE (bottom to top, [radius, y], the caller's units);
the inside is the outside moved in by wall, joined to it by a rounded rim, with a floor wall thick. fill
builds the contents to a level, filling the inside exactly.
volumePoints
function volumePoints(count: number, within: ScatterVolume, size: V3, gap: number, seed = 1): V3[]
Up to count points packed in a volume, at least gap apart (dart throwing), the same for the same seed.
wedgeVerts
function wedgeVerts(): Float32Array<ArrayBuffer>
Wedge (ramp): a unit box halved diagonally — flat floor, vertical back at -x, slope up toward -x.
Classes
Builder
class Builder
Triangle-soup builder for custom geometry (procgen packs use this via world.custom). quad()/triP() self-correct winding — nothing ships inside-out.
triP(a: number[], b: number[], c: number[]): void— One triangle from three (pos+normal, optionally +uv) points — 6 or 8 components each. WINDING IS SELF-CORRECTING: the pipeline culls back faces expecting CCW-from-outside, so if the triangle's geometric normal disagrees with its vertex normals (wound clockwise), it is flipped here — no generator can ship inside-out. Points without a uv are BOX-PROJECTED from the face's dominant axis (unit-box coords → 0..1).quad(a: number[], b: number[], c: number[], d: number[]): void— Two triangles for the quad a-b-c-d (counter-clockwise seen from outside).build(): Float32Array<ArrayBuffer>— Returns the collected vertices as one interleaved Float32Array (8 floats each).
Solid
class Solid
A solid you can boolean against another. Build from any triangle-soup
(a this module's generators generator's output), optionally with a placement, then chain
.subtract() / .union() / .intersect() and hand .toVerts() to
registerMesh3d(k, name, solid.toVerts()) — one baked, instanced mesh.
const die = Solid.fromVerts(roundedBoxVerts(0.22, 3))
.subtract(Solid.fromVerts(sphereVerts(16, 12), { pos: [0, 0.5, 0], scale: 0.2 }));
world.custom('die', () => die.toVerts(), { w: S, h: S, d: S, color });
Bake-time only; keep tools clean/closed (see the robustness note up top).
static fromVerts(verts: Float32Array, xf?: Transform): Solid— Wrap a triangle-soup, optionally baking a placement into it.static merge(solids: Solid[]): Solid— Concatenate solids WITHOUT a boolean. Correct only when they do not overlap each other — and when they do not, it is the right answer, because the union of disjoint solids IS their concatenation. This is the one to reach for when building a tool set. A die's twenty-one pips are twenty-one separate balls:unionwould run twenty BSP booleans to discover they never touch, which is slow enough to stall a page, while this is an array copy. Overlapping tools still wantunion.static union(solids: Solid[]): Solid— Union many solids into one, properly — a full boolean per pair, so overlapping solids come out as a single skin. Combine cutting TOOLS this way before a single subtract: "union the tools, subtract once" keeps the polygon count, and the bake time, an order of magnitude below N sequential subtracts. For tools you know are disjoint,mergedoes the same job without the booleans.union(other: Solid): Solid— Returns a new solid that is this one plusother.subtract(other: Solid): Solid— Returns a new solid withothercut away from this one.intersect(other: Solid): Solid— Returns a new solid: the volume common to this one andother.toVerts(): Float32Array— The result as interleaved (pos, normal, uv) soup forregisterMesh3d.
Types
CoatOptions
interface CoatOptions
How a coat is laid.
thickness: number— How thick, in the target's units.from?: number | undefined— Where its edge sits, 0 (the bottom) … 1 (the top) of the target's height. Default 0.5: the top half.drips?: number | undefined— Drips running down from the edge. Default none.dripDepth?: number | undefined— How far the longest drip runs, in the target's units. Default a fifth of the target's height.wave?: number | undefined— How much the edge waves, in the target's units. Default a thirtieth of the target's height.waves?: number | undefined— Waves round the edge. Default 7.seed?: number | undefined— A different set of drips and waves.under?: boolean | undefined— Cover the bottom instead (the edge rises tofrom, and the part below it is coated).
CurveOptions
interface CurveOptions
How a curve is drawn.
closed?: boolean | undefined— Join the last point back to the first. Default false.corners?: ReadonlyArray<boolean> | undefined— Which control points are corners.smooth?: boolean | undefined— False: straight lines between the points. Default true.maxTurn?: number | undefined— Most degrees the curve may turn between two samples (smaller is smoother). Default 7.maxStep?: number | undefined— Longest step between samples, in the curve's own units. Default: no limit.steps?: number | undefined— Dense samples a span is first cut into, before the gentle ones are dropped. Default 32.
CurveSample
interface CurveSample
One sample of a curve.
p: number[]— The point.at: number— Where it is among the control points: 2.5 is halfway from point 2 to point 3 (radii and widths follow this).corner: boolean— A sharp angle here (a corner point, or an end).
Deform
interface Deform
How to reshape a soup. Every field is optional; they apply in this order: squash, lobes, taper, twist, noise, bend.
bend?: number— Degrees the y axis curves through, as a circular arc about the middle (banana, horn, cucumber).bendAxis?: 'x' | 'z'— Which way it bends: toward +x (z, the default: it turns about z) or toward +z (x).taper?: [number, number]— The section's scale at the top relative to the bottom,[x, z](carrot, cone, trunk).twist?: number— Degrees the section turns about y from bottom to top (licorice, rope).lobes?: { count: number; depth: number; sharp?: number }— The section pushed in and out round y:countlobes,depth0…1 of the radius,sharp0 (round waves) … 1 (narrow creases).noise?: { amount: number; scale?: number; seed?: number }— Smooth lumps along the surface:amountin the soup's units,scalethe size of a lump (default a fifth of the shape),seed.squash?: [number, number]— The section's scale,[x, z]: an oval.
DieOptions
interface DieOptions
How a die is made.
chamfer?: number | undefined— 0 (sharp) … 0.4: how much of each edge is cut away by a chamfer, as a fraction of the die's size. Default 0.12.
DieSides
type DieSides = typeof DIE_SIDES[number]
The number of sides of one die: 4, 6, 8, 10, 12 or 20.
FormDetail
interface FormDetail
How finely a curved form is cut.
around?: number | undefined— Segments round the axis (lathe, vessel) or round the section (sweep). Default 24.maxTurn?: number | undefined— Most degrees a profile or path may turn between samples. Default 7.smooth?: boolean | undefined— False: straight lines between control points. Default true.crease?: number | undefined— Degrees past which two faces meet as a sharp edge rather than a smooth one. Default 45.
Geometry3dApi
interface Geometry3dApi
game.geometry3d: build a named primitive's vertices, or register your own.
readonly names: ReadonlyArray<string>— Every built-in primitive name.build(name: string, opts?: Record<string, number | boolean>): Float32Array— Build a primitive's interleaved vertices (pos xyz, normal xyz, uv — 8 floats a vertex), unit-sized. Throws on an unknown name.define(name: string, build: MeshBuilder): void— Register your own builder under a name, sobuild(name)andworld3d.mesh(name, opts)find it (world-3d builds and uploads it on first ask). Defining a name again replaces the builder:world3d.mesh(name, opts)builds from the new one the next time it is asked, under the same mesh id, so the objects already wearing it change shape too. The same builder again changes nothing. Throws on a built-in primitive's name.has(name: string): boolean— Whether a name is known.
LeafOptions
interface LeafOptions extends FormDetail
How a leaf is shaped.
widths?: ReadonlyArray<number> | undefined— Half-widths along its length, base to tip, 0…1 of the full width (smooth between). Default a classic leaf.fold?: number | undefined— Degrees of the V folded along its midrib.curl?: number | undefined— Degrees it curls along its length (toward its face, +z).twist?: number | undefined— Degrees it twists from base to tip.thickness?: number | undefined— Its thickness, in the caller's units. Default a fortieth of its length.
MeshBuilder
type MeshBuilder = (o: Record<string, number | boolean>) => Float32Array
Builds one primitive's interleaved vertices from an options record.
Placement
interface Placement
One copy's placement: where, which way its +y points, how far it is spun about that, and how big.
pos: V3— Where the copy's origin goes, in world units.up: V3— The direction the copy's +y points; any non-zero length, as it is normalised.spin?: number— Turn aboutup, in radians; default 0.scale?: number | V3— Size: one number for all three axes, or a factor for each of x, y and z; default 1.
ProfilePoint
type ProfilePoint = [number, number] | [number, number, number]
A profile point: [radius, y], with a third value 1 for a corner.
ScatterVolume
type ScatterVolume = 'sphere' | 'cone' | 'box' | 'cylinder' | 'bunch'
A volume copies are packed into, about the origin, size its box.
Soup
type Soup = Float32Array
An interleaved triangle soup: 8 floats a vertex, 3 vertices a triangle.
SoupBounds
interface SoupBounds
The axis-aligned box a soup fills.
min: V3— The smallest x, y and z of any vertex.max: V3— The largest x, y and z of any vertex.
SurfacePoint
interface SurfacePoint
A point on a surface, and which way the surface faces there.
pos: V3— The point on the surface, in the soup's own units.normal: V3— The unit direction the surface faces there.
SurfaceRegion
type SurfaceRegion = 'all' | 'top' | 'side' | 'bottom' | { from: number; to: number }
Which part of a surface: everything, the parts facing up, sideways or down, or a band of its height (0…1).
SweepOptions
interface SweepOptions extends FormDetail
How a shape is swept along a path.
radius: number | ReadonlyArray<number>— Radius (the caller's units): one value, or one per control point, eased between them.section?: SweepSection | undefined— Round (default), a number of flat sides, a star, or a closed outline of[x, z]points.squash?: [number, number] | undefined— The section's scale across[normal, binormal]: an oval.caps?: 'round' | 'flat' | 'point' | 'none' | undefined— How the ends finish:round(default),flat,point, ornone(open).closed?: boolean | undefined— The path loops back to its start (no caps).twist?: number | undefined— Degrees the section turns from start to end.beads?: { count: number; depth: number } | undefined— The radius swells and narrows along the path:countbeads,depth0…1 how much it narrows between them.corners?: ReadonlyArray<boolean> | undefined— Which control points are corners.
SweepSection
type SweepSection = 'round' | number | { star: number; depth: number } | { outline: ReadonlyArray<[number, number]> }
A sweep's cross-section.
Transform
interface Transform
Placement of one solid before it joins the shared CSG space. scale is
uniform or per-axis; rotation follows the shader's roll·pitch·yaw order.
pos?: [number, number, number]— The translation, in world units. Default the origin.scale?: number | [number, number, number]— Uniform (one number) or per-axis scale. Default 1.yaw?: number— Rotation about Y, in radians. Default 0.pitch?: number— Rotation about X, in radians. Default 0.roll?: number— Rotation about Z, in radians. Default 0.
VesselFill
interface VesselFill
What a vessel holds.
level: number— How full: 0 (empty) … 1 (to the brim) of the inside height.surface?: 'flat' | 'dome' | undefined— The top of the contents: flat (a drink) or a dome (a heaped bowl, ice cream). Default flat.
VesselForm
interface VesselForm
The pieces of a vessel.
outside: Soup— The outside wall, the rim and the base.inside: Soup— The inside wall and floor.fill: Soup | null— The contents (a solid filling the inside to the fill level), or null.


