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).
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.
- Paste or type your JavaScript code in the text area
- Run with ▶ Run or Ctrl+Enter
- Navigate through the last 20 executions with Ctrl+↑ / Ctrl+↓ (history cached in RAM + sessionStorage)
- Tab inserts 2 spaces. Scripts natively support
await— NASSCAD automatically wraps them in anasync function
// Global variables
These variables are readable (and sometimes writable) from any script.
| Variable | Type | Description |
|---|---|---|
objs | Array | Array of all scene objects. Each object contains {id, name, type, mesh, color, isHole, genType?, genParams?} |
selObjs | Array | Sub-array of selected objects. Most functions operate on the last element — selObjs[selObjs.length-1] |
isHoleMode | Boolean | Global creation mode. true = new primitives are created as holes (red, semi-transparent) |
objCnt | Number | Auto-incrementing counter used to name objects. Do not modify manually. |
COL | Array<String> | Palette of 10 rotating hex colors: ['#4488cc','#44aa44','#ddaa22','#cc4444','#9966cc',...] |
scene | THREE.Scene | The Three.js scene — direct access if you need low-level control |
camA | Object | Orbital camera parameters: {theta, phi, dist} — dist max: 1600 mm |
camT | THREE.Vector3 | Camera target point (orbit center) |
_camDirty | Boolean | Set to true to force a 3D re-render + dimension canvas redraw |
alignMode | Boolean | Indicates 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
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 textDefault 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
Selects an object. If add=false, clears the current selection first. If add=true, adds to the current selection (equivalent to Shift+click).
| Parameter | Type | Description |
|---|---|---|
obj | Object | A NASSCAD object (element of objs[]) |
add | Boolean | false = 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
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
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
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.
undoPush() first if you want to manage undo manually.// dupSel() sync
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
Modifies position, rotation, or scale of the selected object. Automatically handles ground anchoring for the Y axis. Invalidates the BVH cache.
| Parameter | Values | Description |
|---|---|---|
property | 'position' · 'rotation' · 'scale' | Property to modify |
axis | 'x' · 'y' · 'z' | Target axis |
value | Number (mm or °) | Value in mm (position/scale) or degrees (rotation) |
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
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
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
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
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
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
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
Runs a CSG Boolean operation on selected objects via the Manifold WASM engine. Creates a new result object and removes the sources.
| Parameter | Values | Description |
|---|---|---|
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 |
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 field | Parameter | Description |
|---|---|---|
tore-sr | R (mm) | Ring radius (center → tube center). Default: 34.0 |
tore-st | r (mm) | Tube radius. Must be < R. Default: 5.3 |
tore-sn | N | Ring facets (12–256). Default: 64 |
tore-sm | M | Tube 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 ID | Parameter | Description |
|---|---|---|
pipe-re | Outer radius (mm) | Overall section radius. Default: 5.0 |
pipe-ep | Wall thickness (mm) | r_int = r_ext − e. Default: 1.2 |
pipe-rb | Bend radius (mm) | Default: 12.0 |
pipe-sc | Longitudinal subdiv. | Points on bend arcs. Default: 40 |
pipe-sr | Radial subdiv. | Circular section facets. Default: 16 |
pipe-l1/l2/l3 | Lengths (mm) | Segments 1, 2, 3. Defaults: 30/20/30 |
pipe-v1y/v1z | Bend 1 Y/Z (°) | Angles after segment 1. Defaults: 90/0 |
pipe-v2y/v2z | Bend 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 ID | Parameter | Description |
|---|---|---|
_gearModVal | ISO module (mm) | Global variable. Values: 0.5, 1, 1.5, 2, 2.5, 3, 4, 5, 6, 8. Default: 2 |
gear-gtype | Profile | 'spur' · 'helical' · 'herringbone' |
gear-teeth | Tooth count | Integer ≥ 6. Default: 24. Pitch diameter = module × teeth |
gear-h | Thickness (mm) | Default: 20 |
gear-hr | Bore radius (mm) | Shaft hole radius. 0 = no hole. Default: 6.0 |
gear-tw | Helix 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 ID | Cylind.Gen parameter | DOM ID | Cubic.Gen parameter |
|---|---|---|---|
cc-h | Height H (mm) — default 60 | cbc-w | Width X (mm) — default 60 |
cc-rt | Top radius Rt — default 25 | cbc-h | Height Y (mm) — default 60 |
cc-rb | Bottom radius Rb — default 40 | cbc-d | Depth Z (mm) — default 60 |
cc-ct | Top chamfer — default 5.0 | cbc-c | Chamfer/fillet — default 8.0 |
cc-cb | Bottom chamfer — default 5.0 | cbc-seg | Segments 1/2/6/12/24 — default 6 |
cc-seg | Chamfer segs 1/2/6/12/24/48 | ||
cc-n | Cylinder 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 ID | Parameter | Description |
|---|---|---|
arcsph-r | Radius (mm) | Sphere radius. Default: 20 |
arcsph-phi | Angle phi (°) | Opening angle 1–360°. Default: 180 |
arcsph-seg | Segments | Latitude/longitude subdivisions. Default: 32 |
_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 ID | Parameter | Description |
|---|---|---|
rev-arc | Arc angle (°) | Rotation angle 1–360°. Default: 360 |
rev-seg | Segments | Radial subdivisions. Default: 64 |
showRevDialog();
document.getElementById('rev-arc').value = 180;
document.getElementById('rev-seg').value = 64;
_revRebuild(); await delay(200); _revToScene();
// undoPush() sync
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
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
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
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.
| Variable | Type | Description |
|---|---|---|
camA.theta | radians | Horizontal angle (azimuth). 0 = front view (Z+). Rotates around Y. |
camA.phi | radians | Vertical angle (elevation). 0 = horizontal · π/2 = top view. Default: Math.PI/5 |
camA.dist | mm | Camera → target distance. Default: 240 mm · min: 5 mm · max: 1600 mm |
camT | THREE.Vector3 | Target 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
Sends a message to the NASSCAD Logs console (📋 Logs button). Available levels:
// 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
| Rule | Why |
|---|---|
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 steps | Gives the main thread breathing room — prevents UI freezes |
| Clear scene at the start of demo scripts | Avoids object collisions and memory bloat |
| Keep CSG meshes under 50,000 triangles | Above this — risk of Manifold Worker freeze (> 30 seconds) |
MEM > 70% → flushPool() then save + reload | The MEM gauge (bottom right) — flushPool() frees orphan slots; page reload frees the full GeometryPool |
Call mesh.updateMatrixWorld(true) after each move | Required before any BVH hit-test or bbox calculation |
Set _camDirty = true after mutations | Forces Three.js re-render + 2D dimension canvas redraw |
Call updOList() after mutating objs[] | Syncs the Objects panel — without it, the list is stale |
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