Skip to content

๐Ÿ“– Reference

This is the API surface of Helion as exported from src/index.js. Import via ES modules โ€” no build step required:

import {
Simulation, Vec3, DiscreteScalarField, SurfaceVisualization,
GaussianImpulse, SchrodingerSolver
} from "helion";
// or via importmap in the browser
import { Simulation } from "../../../src/index.js";

See also: Architecture for concepts (State / apply / evolve / bind) and Getting Started for a first simulation.


Factory: Simulation.with(options) โ†’ Simulation instance. All methods are chainable.

OptionTypeDefaultDescription
htmlDivIdstringauto-createdContainer div id
viewport.aspectRatiostring"1 / 1"CSS aspect-ratio for wrapper
camera.positionVec3(3,3,3)Initial camera position
camera.targetVec3(0,0,0)OrbitControls target
camera.fieldOfViewnumber50Perspective FOV
camera.orthographicbooleanfalseOrthographic disables rotate/pan (only zoom)
camera.controlsbooleantrueOrbitControls enabled
scene.backgroundThreeJsScene.BackgroundTRANSPARENTPLAIN, FOG, TRANSPARENT, STARS
scene.backgroundColorhex0x0088ffUsed with PLAIN
scene.scalenumber1Physics โ†’ world mapping
lighting.enabled / shadowsbooleantrue / falseThree.js lights/shadows
headUpDisplay.enabledbooleantrueHUD overlay (simulation.setLatexTitle renders via renderMath in titleDiv)
infoPanel.textstring""Info panel HTML
parameterMenuCollapsedbooleantrueControls collapsed

Chaining API:

Simulation.with({ htmlDivId: "container", scale: 1e-9, headUpDisplay: true })
.runsEvery(0.016) // wall-clock step
.advancesBy(0.01) // simulated time per step
.atSpeed(1) // timeScale multiplier
.substeps(1) // steps per clock tick
.onStep((clock, dt) => { // fixed-step mode
field.evolve(solver, dt);
})
// or: .maxOutCpu((clock) => field.evolve(solver, dt), 30, 10)
.onFrame((t) => {})
.bind(model.alwaysWith(view)) // continuous sync
.bind(model.onceWith(view)) // one-shot sync
.append(new Slider("k").withRange(...)) // controls
.provideAxesAround(view) // axes + AxesUI
.frameSceneOn(view, { padding: 1.2 })
.setOrthographic(true) // switch to orthographic top-down for 2D
.removeAxes() / setAxesVisible(false) // hide axes for 2D
.setLatexTitle("\\phi = n\\cdot137.5\\pi/180") // MathJax/KaTeX via renderMath in titleDiv
.setTextTitle("plain text")
.clearTitle()
.setupGraphWith({ dataDefinition, title })
.plot([t, value])
.withMouseClickEventListener() // click โ†’ start/pause/reset
.appendStartStopResetUI()
.onReset(() => field.reset())
.start() // .stop(), .reset(), .isRunning

runsEvery vs advancesBy: scheduling interval vs simulated time. maxOutCpu(fn, minFrameRate=30, iterationsPerFrame=10) is the adaptive alternative to onStep.

class Transformation { applyTo(model) {} }
model.apply(transformation) // โ†’ transformation.applyTo(model)
model.and(other) // โ†’ BodyPair
model.alwaysWith(view) // โ†’ Binding(ALWAYS)
model.onceWith(view) // โ†’ Binding(ONCE)
simulation.bind(binding) // checks view.canBindTo(model)
new Registry({ id, label, entries: { Key: () => value } })
registry.get(name); registry.names; registry.label; registry.id

Used for ColorMappers, ColorLayers, ShapesFactory.


Mutable vectors: clone(), set, copy, add, sub, addScaledVector, cross, dot, length, normalize, multiplyScalar, distanceTo, projectOnVector, random().

clone(), phase, abs/magnitude/absSquared, multiply(c).

range, normalize(value), scaleUnitParameter(u) โ€” maps [0,1] โ†’ interval, shrinkTo(value).

Iterable: for (const x of new Range(0,10,0.5)). count.

Holds xRange: Interval, yRange: Interval.

linspace(start, stop, num), meshgrid(x, y), factorial(n), degToRad, toCartesian(r,theta,phi), generateUUID(), normalDistribution(mu,sigma), uniform(min,max), randomInt(min,max).


sample(u, v, target) โ€” duck-typed target (number, Complex, Vec3).

field.nx; field.ny; field.data; // Float32Array
field.index(x,y); field.valueAt(x,y); field.setValueAt(x,y,v);
field.reset(); // fill 0
field.evolve(solver, dt); // โ†’ solver.step(this, dt)
field.sample(u,v,target); // bilinear (stub, uses index())
field.nx; field.ny; field.size; field.real; field.imag; // Float32Array
field.index(x,y); field.valueAt(i,j,target); // writes Complex
field.reset(); field.evolve(solver, dt);
const f = new RealFunction({ domain:new Interval(-3,3), func:x=>Math.sin(x)/x });
f.domain; // Interval
f.evaluate(x); // number
f.sample(u, target=new Vec2()); // uโˆˆ[0,1] โ†’ Vec2(x,y)
f.setFunction(newFunc); // for Taylor/Fourier demos

VectorField.sample(positionVector, target). Continuous vs discrete is a data-access detail: field.sample(u,v,target) vs field.valueAt(i,j,target) + field.nx/ny.

frameAt(u, v, target: DifferentialFrame) via DifferentialGeometry. sampleSpacing(resolution).

ParametricSurface({ domain, x=(u,v)=>u, y=(u,v)=>v, z=(u,v)=>0 })

Section titled โ€œParametricSurface({ domain, x=(u,v)=>u, y=(u,v)=>v, z=(u,v)=>0 })โ€

sample maps [0,1]ยฒ โ†’ Domain โ†’ Vec3(x, z, y) (Y-up).

ScalarFieldSurface({ domain, func }) / DiscreteFieldSurface({ field })

Section titled โ€œScalarFieldSurface({ domain, func }) / DiscreteFieldSurface({ field })โ€

ScalarFieldSurface embeds a continuous MultivariateFunction/ComplexFunction as height. DiscreteFieldSurface adapts a DiscreteScalarField via valueAt + central-difference normals โ€” enables SurfaceVisualization for raster fields without manual conversion.


Body({ position, velocity, mass=1, charge=0, fixed=false, orientation })

Section titled โ€œBody({ position, velocity, mass=1, charge=0, fixed=false, orientation })โ€

position/velocity/acceleration/mass/charge/force/state, reset(), integrate(dt, integrator), fieldAt(point), positionVectorTo(other), kineticEnergy, momentum.

RadialSymmetricBody({ position, velocity, mass, radius=1, charge, fixed })

Section titled โ€œRadialSymmetricBody({ position, velocity, mass, radius=1, charge, fixed })โ€

Adds radius.

AxialSymmetricBody({ position, velocity, axis, radius, mass, charge, fixed })

Section titled โ€œAxialSymmetricBody({ position, velocity, axis, radius, mass, charge, fixed })โ€

Adds axis: Vec3.

Block({ position, velocity, size=Vec3(1,1,1), mass, charge, fixed })

Section titled โ€œBlock({ position, velocity, size=Vec3(1,1,1), mass, charge, fixed })โ€

Adds size: Vec3.

Spring network: addBody(body), connect(body1, body2, {k, restLength, damping}), bodyAt(i)/bondAt(i), bodyCount/bondCount, integrate(dt), fixateBodyAt(i), applyToBodies(transformation).

ChainTopology({ count=100, length=20, bondRestLength, totalMass=0.025 })

Section titled โ€œChainTopology({ count=100, length=20, bondRestLength, totalMass=0.025 })โ€

applyTo(lattice) โ€” builds a 1D chain.

CubicLatticeTopology({ nx=4, ny=4, nz=4, spacing=0.3, totalMass=1 })

Section titled โ€œCubicLatticeTopology({ nx=4, ny=4, nz=4, spacing=0.3, totalMass=1 })โ€

applyTo(lattice), index(i,j,k).

PointCloud (particleStateAt(i) โ†’ {position, color, size}) for the legacy PointCloudView / ParticleCloudView pattern. Gas โ€” 2D/3D kinetic gas used in examples/thermodynamics (Gas + ParticleView2D or PointCloudView). Discrete fields (DiscreteScalarField) are now visualized directly via DiscreteFieldSurfaceView/TiledPlane without an intermediate PointCloud.


All forces extend Transformation and accumulate into body.force until body.integrate() consumes it.

ClassConstructorDescription
Forceโ€”Base; _calculateForceOn(body)
FieldForce(field)Samples field at body.position
CoulombForce.in(field)(electricField)F = qยทE
LorentzForce.in(field)(magneticField)F = q vร—B
DragForce(dragCoefficient=-5)F = cยทv_y
UniformGravitationalForce()F = mยทg downward
GravitationalForce()Pair: Gยทm1ยทm2/rยฒ via BodyPair
SpringForce({k=200, restLength, damping=0})Pair: Hooke + damping
CoulombPairForce()Pair: kยทq1ยทq2/rยณยทr
SphereSphereCollision()Pair: elastic collision + penetration resolve

Usage:

body.apply(new DragForce(-2));
bodyA.and(bodyB).apply(new GravitationalForce());
lattice.connect(a, b, { k: 200, restLength: 1, damping: 0.1 });

Constants: G = 6.67e-11, K = 9e9, EC = 1.602e-19, g = 9.81.


All extend Transformation (applyTo(field)). Use declaratively: field.reset().apply(op1).apply(op2).

OperatorOptionsNotes
GaussianImpulse{centerX=100, centerY=100, amplitude=1, sigma=3}Adds Gaussian in ยฑ5px window
GaussianImpulseComplex2D{wavePacketEnergy=0.05, packetWidth=48}Plane wave exp(i kยทr)ยทexp(-rยฒ/wยฒ)
DiamondSquareOperator{roughness=1, amplitude=100}Fractal terrain; nx-1 must be power of 2
PerlinNoiseOperator{scale=50, frequency=0.02, octaves=6, persistence=0.5, z=0}Fractal Perlin via three/ImprovedNoise
DoubleSlitOperator{wavelength=525, positionSlit1, positionSlit2}Interference factor cosยฒ
SineImpulseOperator{wavelengthInPixels=10, amplitude=1, periods=1}Also has .ui()
FFT2D()Forward 2D FFT (rows โ†’ cols)
FFTShift2D()Shift zero-frequency to center
Potential(shapeConfiguration, reflectionStrength=0.1)Sets potential where shape samples true
ShapeMask(shapeConfiguration)Sets 1 where shape samples true
ComplexShapeMask(shapeConfiguration)Sets real to 1
Softness{softness=0}Blur by averaging 4-neighbors
ComplexSoftness{softness=0}Same on real channel
LaplaceOperatorstatic onlyLaplaceOperator.at(field,i,j)
Shapes // { SingleSlit, DoubleSlit, Grating, Circle, Square, Line, Step }
new ShapeConfiguration({ defaultSize=40, defaultShape=Shapes.DoubleSlit })
shapeConfig.size; shapeConfig.shape; shapeConfig.ui() // Dropdown + Slider
ShapesFactory.create(shapeConfig).sample(x, y, field) // โ†’ boolean

BarrierWaveEquation({ obstacleField, velocity=1, damping=0.1 })

Section titled โ€œBarrierWaveEquation({ obstacleField, velocity=1, damping=0.1 })โ€

damping, acceleration(field,i,j) โ€” (1-obstacle)ยทvยฒยทLaplacian, .ui() โ†’ damping slider.

new WaveEquationSolver(equation)
solver.step(field, dt) // field: DiscreteScalarField
solver.reset()
field.evolve(solver, dt)
new SchrodingerSolver(potentialField) // potential.data
solver.initialize(psi, dt) // stagger imag by 0.5 dt
solver.step(psi, dt) // psi: DiscreteComplexField
solver.reset()
psi.evolve(solver, dt)
Body.integrate(dt, Integrators.symplecticEulerStep)
Integrators.eulerStep(state, dt)
Integrators.symplecticEulerStep(state, dt)
Integrators.rk2Step(state, dt, derivativeFn)
Integrators.rk4Step(state, dt)

state: { position, velocity, acceleration, clone() }.


All extend Renderable3D (Object3D). Contract: canBindTo(model), initialize(model), synchronizeWith(model), reset(), dispose(), boundingBox.

ViewOptionsBinds to
Sphere{color=0xffff00, opacity=1, wireframe, segments=24, castShadow}body.position && body.radius
Box{color=0xff0000, opacity, castShadow}body.position && body.size && body.orientation
Cylinder{color, opacity, segments=24, radiusFunction=body=>body.radius}body.position && body.axis
Arrow{color, size=1, opacity, round, magnitudeMap, colorMap}body.position && body.axis
VectorView{vectorProperty=body=>body.velocity, color, size, round}body.position && vectorProperty
Ring{color, thickness=0.1}body.position && body.axis && body.radius
Helix{color, coils=20, thickness=0.05, radiusFunction}BodyPair
Trail{maxPoints=200, trailStep=1, color}body.position
Label{text, color, size}body.position
Floor/Aquarium/Ceilingโ€”decorations

Visibility: prefer particle.visible on the model (e.g. get visible(){return index < n} in FlowerParticle) โ†’ ParticleView2D hides via child meshes and keeps view.visible=true so Binding.synchronize() stays active. Setting view.visible=false skips synchronization (see src/core/helion.js:103 guard) and requires forceSynchronize() to recover.

ViewOptionsNotes
PointCloudView{material}pointCloud.positionAt etc.
LatticeView{bodyViewFactory, bondViewFactory}Lattice โ€” new LatticeView({bodyViewFactory:()=>new Sphere(), bondViewFactory:()=>new SwitchableBondView()})
DiatomicMolecule{bondType, bondColor, atom1Color, atom2Color}BodyPair
SwitchableBondView{bondType="Spring"/"Cylinder", color, coils}delegates to Helix/Cylinder
ArrowField{xRange, yRange, zRange, scaleFactor, magnitudeMap, colorMap}VectorField
ElectromagneticWave{electricFieldColor, magneticFieldColor, numArrows=100}Wave.valueAt
OneDimensionalComplexPlaneWave3D{size, numArrows=70}ComplexWave.valueAt
new SurfaceVisualization({
resolution: new SurfaceResolution(100,100),
colorLayer: new HeightLayer(), // or GaussianCurvatureLayer etc.
colorMapper: colorLayer.preferredColorMapper(),
normalizer: new AdaptiveSymmetricNormalizer(0.05), // or FixedIntervalNormalizer(range)
opacity: 1,
display: SurfaceVisualization.Display.Surface, // "surface"|"glyphs"|"none"
glyphType: GlyphLayer.GlyphTypes.BOXES,
glyphScale: 0.8
})
vis.canBindTo(model) // โ†’ model.frameAt
vis.addOverlayLayer(new ContoursLayer({}))
vis.display(SurfaceVisualization.Display.Glyphs)
vis.ui() // Color map + opacity
vis.colorLayerUI() // Color layer dropdown
  • SurfaceResolution(u=50, v=50)
  • FixedIntervalNormalizer(interval: Interval) โ€” normalize(v) via interval.normalize
  • AdaptiveSymmetricNormalizer(smoothing=0.05) โ€” EMA of maxAbs
  • ColorLayers registry: Height, PrincipalCurvature1/2, GaussianCurvature, MeanCurvature, ShapeIndex, Curvedness
  • Layers: SurfaceLayer, GlyphLayer (BOXES|CAPSULES|CYLINDERS|CONES|ICOSAHEDRONS|TILES|SPHERES), ContoursLayer, PrincipalDirectionsLayer, NormalsLayer
new DiscreteFieldBoxView({ width=200, height=200, heightScale=100, color:0xff0033, opacity:0.35 })
// Instanced BoxGeometry per texel; canBindTo: field.valueAt && field.nx && field.ny
new DiscreteFieldSurfaceView({ colorMapper, opacityFunction }) // 2D DataTexture plane (also listed under 2D)
import { ComplexFunction, ComplexSurfaceView2D, ComplexSurfaceView3D, WaveFunctionSurface3D } from "helion";
// 3D: ComplexFieldViewable base โ†’ ComplexSurfaceView3D (tanh height) / WaveFunctionSurface3D (log height, shader + alpha)
// 2D: ComplexFieldViewable2D base โ†’ ComplexSurfaceView2D (DataTexture, world 4x4 or nx*ny)
const view3D = new ComplexSurfaceView3D({ defaultResolution:new SurfaceResolution(400,400), maxHeight:4 });
const view2D = new ComplexSurfaceView2D({ defaultResolution:new SurfaceResolution(400,400), brightnessFunction:m=>Math.exp(-0.5*m) });
view3D.colorMapper = view2D.colorMapper = ComplexColorMappers.get(ComplexColorMappers.Hsl); // shared control
  • Both share sample(u,v, ComplexFunctionSample{input,output}) / valueAt contract and resolution() logic; valueAt is used for discrete grids (performance) without interpolation.
  • 2D uses brightnessFunction to modulate RGB (kept opaque) to avoid old-frame shine-through.
new PixelRasterView({ width=512, height=512, transparent }) // DataTexture + PlaneGeometry, canBindTo: pixelAt/pixels
new DiscreteFieldSurfaceView({ colorMapper:new WavelengthColorMapper(525), opacityFunction:v=>Math.sqrt(v) })
new FieldEdgeIntensityPixelRaster({ edgeHeight:100, colorMapper, opacityFunction })
new ComplexSurfaceView2D({ showPhaseColour, brightnessFunction:m=>m>1?1:m, colorMapper:ComplexColorMappers.get(ComplexColorMappers.Hsv) })
new ParticleView2D({ segments:16, colorFunction:p=>number, colorMapper:new HueColorMapper(), hasBorder:false, borderColor:Colour.Yellow, visible:true })
// ParticleView2D replaces the legacy InstancedMesh ParticleCloudView; visibility via particle.visible (model) keeps Binding.synchronize() active; hasBorder adds CircleGeometry(1.15) outline
import { RealFunction, Interval, CurveView } from "helion";
const f = new RealFunction({ domain:new Interval(-3,3), func:x=>Math.sin(x) });
const curve = new CurveView({ resolution:200, lineWidth:3, colorMapper:ColorMappers.get(ColorMappers.Uniform) });
simulation.bind(f.alwaysWith(curve)).frameSceneOn(curve);
// update function later: f.setFunction(x=>Math.cos(x));
  • RealFunction is sampled via sample(u, Vec2); CurveView handles valueAt fast-path vs sample like the 2D/3D complex views.
  • Used in examples/mathematics/scenes/taylor_expansion.js and fourier_transform.js (migrated from FunctionGraph).

ColorMappers registry: Gradient, Inferno, RdYlBu, Seismic, Scientific, Terrain, Uniform, Viridis, Water, WaterAlternative. WavelengthColorMapper(lambda=590).


new Slider("Label").withRange(new Range(0,1,0.01)).withValue(0.5).withUnits("m")
new Checkbox("Label").checked(true)
new DropdownMenu().for(registry) // registry: Registry
new RadioGroup().add("Label", cb).checked(0)
new Button().withText("Run").addEventListener("click", cb)
new CompoundControl().add(controlA).add(controlB)
control.togetherWith(other) // same row
control.addEventListener("input", cb) // also calls simulation.onUserInteraction
control.append(div).to(simulation)
simulation.append(control) // โ†’ details panel

Range(from,to,stepSize) used for Slider.withRange. AxesUI(axes).ui() โ†’ Frame/Annotations/XY/XZ/YZ checkboxes.


// Browser (examples use Vite + importmap)
import { Simulation, Vec3 } from "../../../src/index.js";
// NPM
import { Simulation } from "helion";

Build:

Terminal window
npm run build:examples # Vite builds examples/*
npm --prefix docs run build # Astro Starlight โ†’ dist/

Public texture assets: src/textures/; shaders in src/textures/shaders/*.glsl.


This reference mirrors src/index.js exports. For implementation details see source files under src/core, src/model, src/view and the example scenes in examples/.