V4.2.6
NASSCAD Documentation · NassLab 2026 · CC BY-NC 4.0

NassScript — Complete Documentation JavaScript console built into NASSCAD · V4.2.6 · NassLab 2026

NassScript is the JavaScript console built into NASSCAD. It gives full access to every global function of the 3D engine — primitive creation, CSG operations, transforms, colors, camera, and more. Scripts run via new Function() in NASSCAD's global context and natively support await (automatic AsyncFunction wrapping).

⚠ Developer tool only. NassScript runs in NASSCAD's complete JavaScript scope — full access to the scene, Workers, IndexedDB, and all internal functions. Do not execute unverified code.

Everything documented here is plain JavaScript. All NASSCAD variables and functions are accessible directly — no prefix required.


// Console access

Click ⚡ Script in the menu bar — the modal window opens.


// Global variables

These variables are readable (and sometimes writable) from any script.

VariableTypeDescription
objsArrayArray of all scene objects. Each object contains {id, name, type, mesh, color, isHole, genType?, genParams?}
selObjsArraySub-array of selected objects. Most functions operate on the last elementselObjs[selObjs.length-1]
isHoleModeBooleanGlobal creation mode. true = new primitives are created as holes (red, semi-transparent)
objCntNumberAuto-incrementing counter used to name objects. Do not modify manually.
COLArray<String>Palette of 10 rotating hex colors: ['#4488cc','#44aa44','#ddaa22','#cc4444','#9966cc',...]
sceneTHREE.SceneThe Three.js scene — direct access if you need low-level control
camAObjectOrbital camera parameters: {theta, phi, dist} — dist max: 1600 mm
camTTHREE.Vector3Camera target point (orbit center)
_camDirtyBooleanSet to true to force a 3D re-render + dimension canvas redraw
alignModeBooleanIndicates whether alignment mode is active

Object structure

// NASSCAD object structure in objs[]
{
  id:        3,                              // unique identifier
  name:      'Cube_3',                    // display name in the object list
  type:      'cube',                      // 'cube','sphere','cylinder','csg', etc.
  mesh:      THREE.Mesh,                     // direct access to the Three.js mesh
  color:     '#4488cc',                   // hex color
  isHole:    false,                       // true = CSG hole
  genType:   'tore',                      // optional — advanced generator type
  genParams: { R:34, r:5.3, N:64, M:32 }  // optional — generator params
}

// addPrimitive() sync

void addPrimitive(type: string)

Creates a 3D primitive, places it on the grid (y=0) using spiral auto-placement to avoid collisions, and selects it. Respects the global isHoleMode.

Valid values for type

'cube'Box 20×20×20 mm
'sphere'Sphere Ø20 mm
'cylinder'Cylinder Ø20×20 mm
'cone'Cone Ø20×20 mm
'pyramid'Square-base pyramid
'roof'Triangular roof
'roofarc'Arc roof (half-cylinder)
'halfsphere'Half-sphere (dome)
'tube'Hollow tube Ø20/Ø10
'wedge'Beveled wedge
'lathe'Revolved solid (Lathe)
'text'3D extruded text

Default size is 20 mm (PS = 20). To resize, use setDim() or set mesh.scale directly after creation.

// Create a cube
addPrimitive('cube');

// Create a sphere as a hole
isHoleMode = true;
addPrimitive('sphere');
isHoleMode = false;   // back to solid mode

// Get the object just created
addPrimitive('cylinder');
const myObj = objs[objs.length - 1];

// selObj() sync

void selObj(obj: Object, add: Boolean)

Selects an object. If add=false, clears the current selection first. If add=true, adds to the current selection (equivalent to Shift+click).

ParameterTypeDescription
objObjectA NASSCAD object (element of objs[])
addBooleanfalse = exclusive selection · true = add to selection
// Select the first object
selObj(objs[0], false);

// Select by name
const target = objs.find(o => o.name === 'Cube_1');
if(target) selObj(target, false);

// Select all spheres
objs.filter(o => o.type === 'sphere').forEach((o, i) => selObj(o, i > 0));

// selectAllObjs() sync

void selectAllObjs()

Selects all objects in the scene at once. Equivalent to Ctrl+A. Updates the Properties panel and object list.

// Select everything and run a CSG union
selectAllObjs();
await doCSG('union');

// Equivalent direct assignment
selObjs = [...objs];
updProps(); updOList();

// setHoleMode() sync

void setHoleMode(isHole: Boolean)

Switches selected objects between Solid and Hole mode. Also updates the global isHoleMode if no object is selected (changes the creation mode).

// Switch all spheres to holes
objs.filter(o => o.type === 'sphere').forEach(o => {
  selObjs = [o];
  setHoleMode(true);
});

// delSel() sync

void delSel()

Deletes all selected objects. Pushes an Undo snapshot before deletion. Disposes Three.js geometry and material (frees GPU memory). Clears the _csgTree and BVH cache.

Irreversible without Ctrl+Z — always call undoPush() first if you want to manage undo manually.

// dupSel() sync

void dupSel()

Duplicates selected objects. Copies are offset by +10 mm in X and Z. CSG geometry is independently cloned (no shared references).

// Duplicate + reposition manually
addPrimitive('cube');
dupSel();
const copy = objs[objs.length - 1];
copy.mesh.position.x = 50;
copy.mesh.updateMatrixWorld(true);
_camDirty = true;

// setTr() sync

void setTr(property: string, axis: string, value: number)

Modifies position, rotation, or scale of the selected object. Automatically handles ground anchoring for the Y axis. Invalidates the BVH cache.

ParameterValuesDescription
property'position' · 'rotation' · 'scale'Property to modify
axis'x' · 'y' · 'z'Target axis
valueNumber (mm or °)Value in mm (position/scale) or degrees (rotation)
For Y position, NASSCAD automatically computes the offset so the bottom of the object stays at ground level (y=0). The value you pass is the height of the bottom face.
setTr('position', 'x', 30);
setTr('position', 'z', -20);
setTr('position', 'y', 15);  // lift 15 mm off ground
setTr('rotation', 'y', 45);   // 45° around Y

// setDim() sync

void setDim(axis: string, value: number)

Resizes the selected object along the given world axis (AABB), in millimeters. Handles rotated objects via world-axis → local-axis mapping. Automatically repositions on the ground for the Y axis.

addPrimitive('cube');
setDim('x', 50);
setDim('y', 30);
setDim('z', 40);

// recenterCG() sync

void recenterCG()

Recenters the geometry origin on the real center of gravity (centroid of the mesh). The object's visual position is unchanged — only the internal geometry offset is corrected. Useful before CSG or export when the origin must be centered. Pushes an Undo snapshot.

// Center origin of selected object on its CG
selObj(objs[0], false);
recenterCG();

// dropToGround() sync

void dropToGround()

Snaps the selection to the ground plane (Y=0) — adjusts position.y so that the lowest point of the bounding box exactly touches Y=0. Works on multi-selection. Pushes an Undo snapshot.

// Drop all objects to the ground at once
selectAllObjs();
dropToGround();

// setCol() · setOpa() · setWire() sync

void setCol(hexString: string)
void setOpa(opacity: number)
void setWire()
setCol('#ff4444');   // bright red
setOpa(0.5);          // 50% transparent
setWire();             // toggle wireframe

// Direct material access (more flexible)
const o = objs[0];
o.mesh.material.color.set('#22aaff');
o.mesh.material.metalness = 0.8;
o.mesh.material.roughness = 0.1;
_camDirty = true;

// toggleXRay() sync

void toggleXRay()

Toggles X-Ray mode — all objects become semi-transparent with depth-write disabled. Reveals hidden and interior geometry. Same as the 👁 X-Ray toolbar button.

toggleXRay();  // enable X-Ray
await delay(2000);
toggleXRay();  // restore normal view

// renameObj() sync

void renameObj(name: string)

Renames the last selected object. Syncs the _csgTree (updates all parent nodes that reference this object by name). Pushes Undo. No-op if the name is unchanged.

selObjs = [objs[0]];
renameObj('MyBase');

// doCSG() async — await required

async doCSG(op: string)

Runs a CSG Boolean operation on selected objects via the Manifold WASM engine. Creates a new result object and removes the sources.

ParameterValuesDescription
op'union' · 'subtract' · 'intersect''union' — merges N objects into one
'subtract' — subtracts Hole objects from the first Solid
'intersect' — keeps only the volume common to all
Hole-mode rule: if the selection contains a Solid+Hole mix, NASSCAD automatically forces the operation to subtract. You don't need to manually choose subtract in that case.

Precondition: at least 2 objects must be in selObjs. Always await doCSG(...).
// Union of all scene objects
selObjs = [...objs];
await doCSG('union');

// Cube drilled by a sphere (auto-subtract)
addPrimitive('cube');
setDim('x', 40); setDim('y', 40); setDim('z', 40);

isHoleMode = true;
addPrimitive('sphere');
setDim('x', 30);
isHoleMode = false;

selObjs = [...objs];
await doCSG('union');   // becomes subtract automatically

// Tore.Gen — Ultra-HD Torus

Parametric watertight torus. Periodic topology (i+1)%N · (j+1)%M — no seam, no edge, fully Manifold WASM compatible.

DOM fieldParameterDescription
tore-srR (mm)Ring radius (center → tube center). Default: 34.0
tore-str (mm)Tube radius. Must be < R. Default: 5.3
tore-snNRing facets (12–256). Default: 64
tore-smMTube facets (6–128). Default: 32
showToreDialog();
document.getElementById('tore-sr').value = 50;
document.getElementById('tore-vr').textContent = '50.0';
document.getElementById('tore-st').value = 12;
document.getElementById('tore-vt').textContent = '12.0';
document.getElementById('tore-sn').value = 128;
document.getElementById('tore-vn').textContent = '128';
document.getElementById('tore-sm').value = 64;
document.getElementById('tore-vm').textContent = '64';
_toreRebuild();
await delay(200);
_toreToScene();

// Pipe.Gen — Hollow pipe

Hollow 3-segment pipe with bends. Bishop frame algorithm (parallel transport) + tangent circular arcs — zero section twist. Watertight manifold.

DOM IDParameterDescription
pipe-reOuter radius (mm)Overall section radius. Default: 5.0
pipe-epWall thickness (mm)r_int = r_ext − e. Default: 1.2
pipe-rbBend radius (mm)Default: 12.0
pipe-scLongitudinal subdiv.Points on bend arcs. Default: 40
pipe-srRadial subdiv.Circular section facets. Default: 16
pipe-l1/l2/l3Lengths (mm)Segments 1, 2, 3. Defaults: 30/20/30
pipe-v1y/v1zBend 1 Y/Z (°)Angles after segment 1. Defaults: 90/0
pipe-v2y/v2zBend 2 Y/Z (°)Angles after segment 2. Defaults: 0/45
// L-shaped pipe (90° bend in Y)
showPipeDialog();
document.getElementById('pipe-re').value  = 8;
document.getElementById('pipe-ep').value  = 1.5;
document.getElementById('pipe-rb').value  = 20;
document.getElementById('pipe-l1').value  = 50;
document.getElementById('pipe-v1y').value = 90;
document.getElementById('pipe-l3').value  = 50;
_pipeRebuild();
await delay(300);
_pipeToScene();

// Gear.Gen — Gears & Pulleys

ISO gears (involute profile, α=20°) — spur / helical / herringbone. V-groove pulleys (watertight lathe) and timing pulleys (ExtrudeGeometry). Standard ISO modules (0.5 → 8 mm).

Gear mode

DOM IDParameterDescription
_gearModValISO module (mm)Global variable. Values: 0.5, 1, 1.5, 2, 2.5, 3, 4, 5, 6, 8. Default: 2
gear-gtypeProfile'spur' · 'helical' · 'herringbone'
gear-teethTooth countInteger ≥ 6. Default: 24. Pitch diameter = module × teeth
gear-hThickness (mm)Default: 20
gear-hrBore radius (mm)Shaft hole radius. 0 = no hole. Default: 6.0
gear-twHelix angle (°)For helical/herringbone. Default: 20
// Spur gear m2 z24
showGearDialog();
_gearModVal = 2;
document.getElementById('gear-gtype').value = 'spur';
document.getElementById('gear-teeth').value = 24;
document.getElementById('gear-h').value     = 15;
document.getElementById('gear-hr').value    = 5;
_gearRebuild();
await delay(400);
_gearToScene();

// Cylind.Gen · Cubic.Gen

Truncated cylinder with double chamfer/fillet and chamfered/rounded cube. Both guarantee watertight manifold output.

DOM IDCylind.Gen parameterDOM IDCubic.Gen parameter
cc-hHeight H (mm) — default 60cbc-wWidth X (mm) — default 60
cc-rtTop radius Rt — default 25cbc-hHeight Y (mm) — default 60
cc-rbBottom radius Rb — default 40cbc-dDepth Z (mm) — default 60
cc-ctTop chamfer — default 5.0cbc-cChamfer/fillet — default 8.0
cc-cbBottom chamfer — default 5.0cbc-segSegments 1/2/6/12/24 — default 6
cc-segChamfer segs 1/2/6/12/24/48
cc-nCylinder facets 12/16/24/32/64 — default 32
// Cylind.Gen
showCylindDialog();
document.getElementById('cc-h').value  = 80;
document.getElementById('cc-rt').value = 20;
document.getElementById('cc-rb').value = 35;
_ccRebuild(); await delay(200); _ccToScene();

// ArcSphere.Gen — Spherical sector

Spherical sector with configurable opening angle phi (1–360°). Capped with analytically-correct normals at both poles. Manifold-verified.

DOM IDParameterDescription
arcsph-rRadius (mm)Sphere radius. Default: 20
arcsph-phiAngle phi (°)Opening angle 1–360°. Default: 180
arcsph-segSegmentsLatitude/longitude subdivisions. Default: 32
At phi=360° the result is a full sphere. At phi=180° it is a half-sphere with a flat cap. The _arcSphNoMerge sentinel (try/finally protected) ensures the cap normal is never merged incorrectly.
showArcSphDialog();
document.getElementById('arcsph-r').value   = 30;
document.getElementById('arcsph-phi').value = 270;
_arcSphRebuild(); await delay(200); _arcSphToScene();

// RevSolid.Gen — Revolved solid

Revolved solid (lathe) with arc angle 1–360°. Closed planar caps when angle < 360°. Watertight manifold.

DOM IDParameterDescription
rev-arcArc angle (°)Rotation angle 1–360°. Default: 360
rev-segSegmentsRadial subdivisions. Default: 64
showRevDialog();
document.getElementById('rev-arc').value = 180;
document.getElementById('rev-seg').value = 64;
_revRebuild(); await delay(200); _revToScene();

// undoPush() sync

void undoPush(label: string?)

Saves a snapshot of the current state to the Undo stack. 200 levels persisted in IndexedDB via structured clone (TypedArrays native — ~20–50× faster than base64 JSON). The _csgTree is included in each snapshot — Re-run stays coherent after Ctrl+Z. Call this before any destructive modification.

// newProject() sync

void newProject()

Completely clears the scene, resets counters, empties Undo/Redo stacks and IDB journal, clears the CSG Tree and GeometryPool GC. Ideal at the start of a script for a clean slate.

// updStats() · updOList() sync

void updStats()
void updOList()

updStats() updates the Vertices / Faces / Objects counters in the toolbar. updOList() rebuilds the object list in the Objects panel. Always call updOList() after updStats() when you've manually mutated objs[].

// flushPool() sync

void flushPool()

Triggers a manual mark-and-sweep GC on the GeometryPool. Frees all orphan geometry slots (objects removed from objs[] but not yet GC'd). Displays freed MB in the Logs console. Same as the ♻ Flush toolbar button.

// Check memory before/after a heavy CSG sequence
flushPool();
await doCSG('union');
flushPool();

// Orbital camera

The camera is defined by spherical coordinates (theta, phi, dist) around the target point camT.

VariableTypeDescription
camA.thetaradiansHorizontal angle (azimuth). 0 = front view (Z+). Rotates around Y.
camA.phiradiansVertical angle (elevation). 0 = horizontal · π/2 = top view. Default: Math.PI/5
camA.distmmCamera → target distance. Default: 240 mm · min: 5 mm · max: 1600 mm
camTTHREE.Vector3Target point (orbit center). Default: {0, 0, 0}
// Top view
camA.phi = Math.PI / 2 - 0.01;
updCam(); _camDirty = true;

// Cinematic rotation (8 seconds)
const delay = ms => new Promise(r => setTimeout(r, ms));
let t = 0;
const spin = setInterval(() => {
  camA.theta += 0.015;
  camA.phi = Math.PI / 5 + Math.sin(t) * 0.12;
  camA.dist = 300 + Math.sin(t * 0.7) * 40;
  t += 0.04;
  updCam(); _camDirty = true;
}, 16);
await delay(8000);
clearInterval(spin);

// nasLog() sync

void nasLog(level: string, message: string)

Sends a message to the NASSCAD Logs console (📋 Logs button). Available levels:

'OK''INFO''WARN''ERROR''CSG''DBG'

// delay() async

const delay = ms => new Promise(r => setTimeout(r, ms));

Non-blocking pause — essential for animations and giving the engine breathing room between heavy operations. Declare at the top of your script.


// Example: box drilled by a cylinder

60×40×60 mm box with a centered Ø20 mm cylindrical hole.

// ── Drilled box — NassScript V4.2.6 ─────────────────
const delay = ms => new Promise(r => setTimeout(r, ms));

// Clear scene
objs.forEach(o => {
  scene.remove(o.mesh);
  o.mesh.geometry.dispose();
  o.mesh.material.dispose();
});
objs = []; selObjs = []; objCnt = 0;
updOList(); updStats();
nasLog('OK', 'Scene cleared');

// 1. Main box
isHoleMode = false;
addPrimitive('cube');
setDim('x', 60); setDim('y', 40); setDim('z', 60);
nasLog('OK', 'Box 60×40×60 mm');
await delay(200);

// 2. Drill cylinder (Hole mode)
isHoleMode = true;
addPrimitive('cylinder');
setDim('x', 20); setDim('z', 20); setDim('y', 50);
objs[objs.length-1].mesh.position.set(0, -5, 0);
objs[objs.length-1].mesh.updateMatrixWorld(true);
isHoleMode = false;
nasLog('OK', 'Drill Ø20 mm');
await delay(200);

// 3. CSG
selObjs = [...objs];
nasLog('CSG', 'Subtraction…');
await doCSG('union');
nasLog('OK', '✓ Done!');

// Example: hole grid with a loop

100×5×80 mm plate drilled with a 4×3 grid of Ø8 mm holes.

// ── Hole-grid plate — NassScript V4.2.6 ─────────────
const delay = ms => new Promise(r => setTimeout(r, ms));

objs.forEach(o => { scene.remove(o.mesh); o.mesh.geometry.dispose(); o.mesh.material.dispose(); });
objs = []; selObjs = []; objCnt = 0;
updOList(); updStats();

isHoleMode = false;
addPrimitive('cube');
setDim('x', 100); setDim('y', 5); setDim('z', 80);
nasLog('OK', 'Plate created');

const COLS=4, ROWS=3, spacX=20, spacZ=20;
const offX = -(COLS-1)*spacX/2;
const offZ = -(ROWS-1)*spacZ/2;

isHoleMode = true;
for(let r=0; r<ROWS; r++){
  for(let c=0; c<COLS; c++){
    addPrimitive('cylinder');
    const hole = objs[objs.length-1];
    setDim('x',8); setDim('z',8); setDim('y',20);
    hole.mesh.position.set(offX+c*spacX, -8, offZ+r*spacZ);
    hole.mesh.updateMatrixWorld(true);
    await delay(50);
  }
}
isHoleMode = false;
_camDirty = true;

selObjs = [...objs];
nasLog('CSG', `CSG — ${objs.length} objects`);
await doCSG('union');
nasLog('OK', '✓ Drilled plate complete!');

// Official demo: Stellate Crown

Central torus, 6 orbiting spheres, CSG subtraction, cinematic camera rotation.

// ── Stellate Crown — NassScript V4.2.6 ──────────────
const delay = ms => new Promise(r => setTimeout(r, ms));

// 1. Clear
objs.forEach(o => { scene.remove(o.mesh); o.mesh.geometry.dispose(); o.mesh.material.dispose(); });
objs = []; selObjs = []; objCnt = 0;
updOList(); updStats();
nasLog('OK', '🎬 Demo started');

// 2. Central torus R=50 r=12 N=128 M=64
showToreDialog();
['tore-sr','tore-vr'].forEach((id,i)=>document.getElementById(id)[i?'textContent':'value']='50');
['tore-st','tore-vt'].forEach((id,i)=>document.getElementById(id)[i?'textContent':'value']='12');
['tore-sn','tore-vn'].forEach((id,i)=>document.getElementById(id)[i?'textContent':'value']='128');
['tore-sm','tore-vm'].forEach((id,i)=>document.getElementById(id)[i?'textContent':'value']='64');
_toreRebuild(); await delay(300); _toreToScene();
nasLog('OK', 'Central torus placed');
await delay(500);

// 3. 6 orbiting spheres at R=70 mm
const N=6, R_orbit=70;
for(let i=0; i<N; i++){
  const angle = (2*Math.PI*i)/N;
  isHoleMode = false;
  addPrimitive('sphere');
  const o = objs[objs.length-1];
  o.mesh.position.x = Math.cos(angle)*R_orbit;
  o.mesh.position.z = Math.sin(angle)*R_orbit;
  o.mesh.position.y = 8;
  o.mesh.scale.setScalar(0.45);
  o.mesh.updateMatrixWorld(true);
  updStats(); _camDirty = true;
  nasLog('OK', `Sphere ${i+1}/${N}`);
  await delay(300);
}

// 4. Switch spheres to Hole mode
for(let i=1; i<objs.length; i++){
  selObjs = [objs[i]]; setHoleMode(true); await delay(150);
}

// 5. CSG
selObjs = [...objs];
nasLog('CSG', '⚙ Union (auto-subtract)…');
await doCSG('union');
await delay(800);

// 6. Cinematic spin (8 seconds)
nasLog('OK', '🎥 Camera spin…');
let t=0;
const spin = setInterval(()=>{
  camA.theta += 0.015;
  camA.phi = Math.PI/5 + Math.sin(t)*0.12;
  camA.dist = 350 + Math.sin(t*0.7)*40;
  t += 0.04;
  updCam(); _camDirty = true;
}, 16);
await delay(8000);
clearInterval(spin);
nasLog('OK', '🎬 Demo complete!');

// Limits & best practices

RuleWhy
Always await doCSG()doCSG is async (Manifold WASM Worker). Without await, the script continues before the result is ready → objs[] becomes inconsistent
Use delay() between heavy stepsGives the main thread breathing room — prevents UI freezes
Clear scene at the start of demo scriptsAvoids object collisions and memory bloat
Keep CSG meshes under 50,000 trianglesAbove this — risk of Manifold Worker freeze (> 30 seconds)
MEM > 70% → flushPool() then save + reloadThe MEM gauge (bottom right) — flushPool() frees orphan slots; page reload frees the full GeometryPool
Call mesh.updateMatrixWorld(true) after each moveRequired before any BVH hit-test or bbox calculation
Set _camDirty = true after mutationsForces Three.js re-render + 2D dimension canvas redraw
Call updOList() after mutating objs[]Syncs the Objects panel — without it, the list is stale
Direct Three.js access: since NassScript runs in NASSCAD's global context, you have direct access to THREE, scene, cam, and all internal helpers — you can create custom Three.js geometries and add them to the scene manually if needed.

NassScript Documentation V4.2.6 · NassLab 2026 · CC BY-NC 4.0 · nasscad.com