Agent skill
threedee-parametric
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/development/threedee-parametric
SKILL.md
threedee Parametric Design
A live-updating 3D viewport for parametric modeling with AI assistance. Edit code in VS Code, see results instantly in the browser.
Project Structure
threedee/
├── src/
│ ├── main.js # Main scene, camera, lighting, model loader
│ └── exporters.js # STL/OBJ/glTF export utilities
├── user/
│ ├── models/ # User's parametric model definitions (auto-discovered!)
│ └── exports/ # Generated mesh files (STL, OBJ, glTF)
└── index.html # Viewport UI with model selector
Creating New Models (Recommended Workflow)
Models are automatically discovered! Simply create a new .js file in user/models/ and it will appear in the UI dropdown after a page refresh.
Step 1: Create a Model File
Create a new file in user/models/ with a descriptive kebab-case name (e.g., my-bracket.js).
The filename becomes the model ID and is converted to Title Case in the UI.
Step 2: Export Required Functions
Every model file must export a createMesh() function:
// user/models/my-bracket.js
import * as THREE from 'three';
export const parameters = {
width: 40,
height: 20,
thickness: 5,
color: 0x4a90d9,
};
export function createMesh(userParams = {}) {
const params = { ...parameters, ...userParams };
const geometry = new THREE.BoxGeometry(
params.width,
params.height,
params.thickness
);
const material = new THREE.MeshStandardMaterial({ color: params.color });
const mesh = new THREE.Mesh(geometry, material);
mesh.castShadow = true;
mesh.receiveShadow = true;
mesh.position.y = params.height / 2; // Sit on ground plane
return mesh;
}
Step 3: Save and View
- Save the file
- The browser will hot-reload (or refresh if needed)
- Select your new model from the MODEL dropdown in the top-left
- Export when ready using the EXPORT MODEL panel
Quick Reference
Basic Primitives
const geometry = new THREE.BoxGeometry(2, 1, 3); // width, height, depth
const geometry = new THREE.SphereGeometry(1, 32, 32); // radius, segments
const geometry = new THREE.CylinderGeometry(1, 1, 2, 32); // topR, bottomR, height
const geometry = new THREE.TorusGeometry(1, 0.4, 16, 100); // radius, tube, segments
const geometry = new THREE.TorusKnotGeometry(1, 0.35, 128, 32);
Common Modifications
Change object color:
material.color.setHex(0x22d3ee); // cyan accent
Position object:
mesh.position.y = 1.5; // lift above ground
mesh.position.set(0, 1.5, 0); // x, y, z
Scale object:
mesh.scale.set(2, 1, 1); // stretch in x
Rotate object:
mesh.rotation.y = Math.PI / 4; // 45 degrees
Parametric Pattern
For reusable models, always merge with default parameters to handle partial updates correctly:
export const parameters = {
width: 40,
height: 20,
depth: 40,
color: 0x4a90d9,
};
export function createGeometry(userParams = {}) {
const params = { ...parameters, ...userParams };
return new THREE.BoxGeometry(params.width, params.height, params.depth);
}
export function createMesh(userParams = {}) {
const params = { ...parameters, ...userParams };
const geometry = createGeometry(params);
const material = new THREE.MeshStandardMaterial({ color: params.color });
return new THREE.Mesh(geometry, material);
}
Geometry Merging Best Practices
When combining different geometry types (e.g., mixing BoxGeometry with ExtrudeGeometry), they often have incompatible index buffers which causes mergeGeometries to fail.
Always convert to non-indexed before merging to ensure compatibility:
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
// ... create geometries ...
// Convert all to non-indexed before merging to avoid attribute/index conflicts
const compatibleGeometries = [geo1, geo2, geo3].map(g => g.toNonIndexed());
const merged = mergeGeometries(compatibleGeometries);
// Optional: Weld vertices afterwards if smooth shading is needed
// import { mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
// const smooth = mergeVertices(merged);
Exporting Models
Export the active 3D model to various formats using the browser console or programmatically:
Single Format Exports
// STL (3D printing)
threedeeExport.stl(mesh, "my-part") // Binary (recommended)
threedeeExport.stlAscii(mesh, "my-part") // ASCII text format
// General interchange
threedeeExport.obj(mesh, "my-part") // OBJ with UVs and normals
// Web and AR
threedeeExport.gltf(mesh, "my-part") // glTF (JSON)
threedeeExport.glb(mesh, "my-part") // GLB (binary, compact)
// CAD interchange
threedeeExport.step(mesh, "my-part") // STEP Faceted B-Rep
Batch Export (All Formats)
// Export to STL, OBJ, glTF, and STEP simultaneously
await threedeeExport.all(mesh, "my-part")
// Export to specific formats
await threedeeExport.all(mesh, "my-part", ['stl', 'step', 'obj'])
Export Notes
- Files are automatically timestamped:
my-part_2025-12-30_23-02-15.stl - Browser downloads are triggered automatically
- Console logs confirm successful exports
- The
meshvariable is always available in the console and accessible viawindow.mesh
Lighting Adjustment
keyLight.intensity = 1.5; // brighter
keyLight.position.set(10, 15, 10); // move light
rimLight.color.setHex(0xff6600); // orange rim
Advanced Modeling & Slicer Compatibility
When creating complex procedural shapes (like gyroids or lattices) intended for 3D printing, follow these best practices to ensure "watertight" models and high-quality exports.
Marching Cubes Best Practices
If using MarchingCubes, configure it for high resolution and clean boundaries:
- Polygon Budget: Always set a high
maxPolyCount(e.g.,1,000,000) for high-resolution meshes to prevent model sections from being "cut off" due to buffer limits. - Watertight Boundaries: Use padding in your scalar field. Never let the isosurface (boundary) sit exactly on the domain edge. Sample a slightly larger domain than the actual object size to ensure the Marching Cubes algorithm can "close" the mesh.
- Boundary Smoothing: When clipping shapes (e.g., using
Math.maxfor a cylinder boundary), use a multiplier (e.g.,* 5or* 10) for the boundary distance to create a smooth gradient that the interpolator can follow. - Vertex Welding: Always use
mergeVerticesfromBufferGeometryUtilsaftermc.update()to weld coincident vertices. This fixes "cracks" and "open edges" that slicers often report.
import { mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
// ... inside createMesh ...
const mc = new MarchingCubes(resolution, material, false, false, 1000000);
// ... fill field ...
mc.update();
// Weld and recompute normals
const welded = mergeVertices(mc.geometry, 1e-5);
welded.computeVertexNormals();
mc.geometry.dispose();
mc.geometry = welded;
Ensuring Export Quality
The src/exporters.js utilities include an automated preparation step for STL exports that handles:
- Coordinate system normalization (baking world transforms).
- Vertex welding and normal correction.
- Removal of degenerate (zero-area) triangles.
Workflow
Creating vs Iterating
IMPORTANT: When asked to improve, refine, or iterate on an existing model:
- DO: Edit the existing model file directly (e.g., modify
user/models/tree.js) - DON'T: Create new files like
tree-v2.js,tree-improved.js,realistic-tree.js
Only create a new file when the user explicitly asks for a new, separate model.
Standard Workflow
- User requests a new 3D object → Create a new file in
user/models/ - User requests improvements/changes → Edit the existing model file
- Save — the viewport hot-reloads instantly
- The user's selected model persists across page refreshes
- Export when ready via the EXPORT MODEL panel
Iteration Examples
User says: "Make the tree more realistic"
Action: Edit user/models/tree.js to improve the existing code
User says: "Add more branches and better textures"
Action: Edit user/models/tree.js to add the requested features
User says: "Create a pine tree (separate from the oak tree)"
Action: Create a new file user/models/pine-tree.js
Console Commands
Models can also be loaded programmatically:
// Load a model by ID (filename without .js)
await loadModel('shelf-bracket');
// List all available models
console.log(modelList);
// Access current mesh
window.mesh
Important Notes
- The viewport runs at
http://localhost:3002(or next available port) - Models are auto-discovered from
user/models/*.js— no manual registration needed! - Selected model persists across page refreshes via localStorage
- Model filenames use kebab-case (e.g.,
my-widget.js) and display as Title Case in the UI - Y-axis is up (standard Three.js convention)
- Objects should be positioned with
y > 0to sit above the grid - The
meshvariable is exported and accessible in console viawindow.mesh - All changes hot-reload instantly—no refresh needed
- When iterating: edit the existing file, don't create new versions
References
For advanced geometry and CSG operations, see references/geometry.md.
Didn't find tool you were looking for?