scad-js
Version:
Generate OpenSCAD solid models with typescript
308 lines (231 loc) • 9.92 kB
Markdown
<h1 align="center">
<img src="https://i.imgur.com/IiI57LR.png" alt="scad-js" height="128"> SCAD-JS
</h1>
> **scad-js** transpile your TypeScript to **OpenSCAD** letting you create programmatic 3D solid models with the familiar TypeScript/JavaScript syntax.
<p align="center">
<a href="https://www.npmjs.com/package/scad-js">
<img alt="Latest release" src="https://img.shields.io/npm/v/scad-js?style=for-the-badge">
<img alt="Codecov coverage" src="https://img.shields.io/codecov/c/github/scad-js/scad-js?style=for-the-badge">
</a>
<a href="https://www.npmjs.com/package/scad-js">
<img alt="scad-js demo" src="https://i.imgur.com/GhjNUxM.gif">
</a>
</p>
**OpenSCAD** is an amazing software for creating solid 3D CAD objects, but modeling with the **OpenSCAD** language can be really cumbersome and limited.
**scad-js** overcomes these limitations with the power of TypeScript, providing full type safety for your 3D models.
## Getting started
First make sure you have [OpenSCAD](https://www.openscad.org/downloads.html) installed on your system, we will use it to visualize the model.
You can create a new project from scratch:
```bash
mkdir my-scad-project
cd my-scad-project
bun init -y
bun add scad-js typescript
```
Or clone the starter template:
```bash
git clone https://github.com/scad-js/scad-js-starter.git my-scad-js-project
cd my-scad-js-project
bun install
```
## Using with TypeScript
Create a simple model with TypeScript:
```typescript
import { cube, sphere, cylinder, difference, union } from 'scad-js';
import * as fs from 'fs';
// Create a base cube
const base = cube(10).translate([0, 0, 0]);
// Create a sphere for the top
const top = sphere(3).translate([0, 0, 10]);
// Create cylindrical holes
const hole1 = cylinder(20, 1).translate([3, 3, -5]);
const hole2 = cylinder(20, 1).translate([-3, 3, -5]);
const hole3 = cylinder(20, 1).translate([3, -3, -5]);
const hole4 = cylinder(20, 1).translate([-3, -3, -5]);
// Combine everything using operations
const model = difference(
union(base, top),
hole1, hole2, hole3, hole4
);
// Serialize to OpenSCAD code
const scadCode = model.serialize();
// Save the result to a file
fs.writeFileSync('model.scad', scadCode);
// Or render directly to STL (requires openscad-wasm)
const stlData = await model.render();
fs.writeFileSync('model.stl', stlData);
```
Then run:
```bash
bun run your-model.ts
```
This will generate an OpenSCAD file that you can open and render, or directly generate an STL file.
## Documentation
For detailed documentation on how to use scad-js visit [scad-js-docs](https://github.com/scad-js/scad-js-docs), you can also look at the official [OpenSCAD Documentation](https://www.openscad.org/documentation.html) page.
## Type Safety
scad-js provides TypeScript definitions that help you catch errors early:
- Vector dimensions ([2D] or [3D]) are type-checked
- SCAD primitives and operations have correct parameter types
- Transformation methods are properly typed
- Intellisense support in most editors
## Examples
Here are some examples to help you get started with scad-js:
### Simple Example
This example demonstrates basic operations like union, difference, and combining primitive shapes:
```typescript
import { cube, sphere, cylinder, difference, union } from 'scad-js';
import * as fs from 'fs';
// Create a simple model with TypeScript
const createModel = (): string => {
// Create a base cube
const base = cube(10).translate([0, 0, 0]);
// Create a sphere for the top
const top = sphere(3).translate([0, 0, 10]);
// Create cylindrical holes
const hole1 = cylinder(20, 1).translate([3, 3, -5]);
const hole2 = cylinder(20, 1).translate([-3, 3, -5]);
const hole3 = cylinder(20, 1).translate([3, -3, -5]);
const hole4 = cylinder(20, 1).translate([-3, -3, -5]);
// Combine everything using operations
const model = difference(
union(base, top),
hole1, hole2, hole3, hole4
);
// Serialize to OpenSCAD code
return model.serialize();
};
// Generate the OpenSCAD code
const scadCode = createModel();
// Save the result to a file
fs.writeFileSync('model.scad', scadCode);
```
### Hollowed Cube (Simple Version)
A cube with a spherical cavity, created using a single sphere subtraction:
```typescript
import { cube, sphere, difference } from 'scad-js';
import * as fs from 'fs';
// Create a hollowed cube model using a simple sphere subtraction
const createHollowedCubeSimple = (): string => {
// Size parameters
const cubeSize = 20;
const sphereRadius = 16; // Bigger than half the cube size to create large holes
// Create the main cube
const mainCube = cube(cubeSize);
// Create the sphere
const innerSphere = sphere(sphereRadius);
// Combine using difference operation to cut out the sphere from the cube
const model = difference(mainCube, innerSphere)
.translate([-cubeSize/2, -cubeSize/2, -cubeSize/2]); // Center the model
// Set color to yellow/gold
const coloredModel = (model as any).color([1, 0.8, 0]);
// Serialize to OpenSCAD code
return coloredModel.serialize();
};
// Generate the OpenSCAD code
const scadCode = createHollowedCubeSimple();
fs.writeFileSync('hollowed-cube-simple.scad', scadCode);
```
### Parametric Tower Generator (Advanced)
This example showcases the true power of JavaScript for generative 3D modeling by creating complex architectural structures:
```typescript
import { cube, cylinder, sphere, union, difference, hull, type ScadObject } from 'scad-js';
import { writeFileSync } from 'fs';
// Tower configuration - easily customizable parameters
interface TowerConfig {
floors: number;
floorHeight: number;
baseWidth: number;
topWidth: number;
windowsPerFloor: number;
curveIntensity: number; // Creates organic curves
balconyEveryNFloors: number;
}
const config: TowerConfig = {
floors: 12,
floorHeight: 4,
baseWidth: 20,
topWidth: 12,
windowsPerFloor: 8,
curveIntensity: 0.3,
balconyEveryNFloors: 3
};
// Mathematical curve calculation for organic shapes
function calculateFloorWidth(floorIndex: number, totalFloors: number, baseWidth: number, topWidth: number, curveIntensity: number): number {
const linearRatio = floorIndex / (totalFloors - 1);
const curveOffset = Math.sin(linearRatio * Math.PI) * curveIntensity * (baseWidth - topWidth) * 0.3;
const width = baseWidth + (topWidth - baseWidth) * linearRatio + curveOffset;
return Math.max(width, topWidth * 0.8);
}
// Generate multiple tower variations from the same codebase
const towers = [
{ name: 'classic_tower', config: { ...config } },
{ name: 'curved_tower', config: { ...config, curveIntensity: 0.8, floors: 15 } },
{ name: 'modern_tower', config: { ...config, windowsPerFloor: 12, floors: 18 } }
];
// Generate all variations using JavaScript loops and math
towers.forEach(({ name, config }) => {
const tower = generateParametricTower(config);
writeFileSync(`${name}.scad`, tower.serialize({ $fn: 50 }));
});
```
**This example demonstrates:**
- 🔄 **Loops** for generating repetitive elements (floors, windows)
- 📐 **Mathematical calculations** for organic curves and precise positioning
- 🎛️ **Conditional logic** for feature placement (balconies, decorative elements)
- 🧩 **Modular functions** for reusable components
- 📊 **Data structures** for managing complex geometry collections
- ⚙️ **Parameterization** for instant design variations
- 🎨 **Multiple outputs** from a single codebase
*This level of parametric modeling would be extremely difficult to achieve in pure OpenSCAD!*
### Functional Design Examples
**scad-js** excels at creating practical, real-world objects with complex engineering requirements:
#### 📱 Parametric Phone Stand
```typescript
// Customizable for any device size and viewing angle
const phoneStand = generatePhoneStand({
phoneWidth: 75,
standAngle: 60, // Perfect viewing angle
cableSlot: true, // Charging cable management
weightingHoles: true, // Add coins for stability
rubberGrips: true // Non-slip base pads
});
// Generate multiple variations for different use cases
const variations = [
{ name: 'desk_work', angle: 45, depth: 60 },
{ name: 'video_calls', angle: 75, height: 70 },
{ name: 'bedside', angle: 55, compact: true }
];
```
#### 🖊️ Modular Pen Holder
```typescript
// Smart compartment arrangement with automatic layout
const penHolder = generatePenHolder({
compartments: [
{ type: 'pen', quantity: 3 },
{ type: 'marker', quantity: 2 },
{ type: 'business_cards', quantity: 1 },
{ type: 'scissors', quantity: 1 }
],
phoneSlot: true, // Built-in phone stand
drawerSlot: true, // Hidden storage drawer
cableManagement: true, // Wire routing holes
labelAreas: true // Raised text areas
});
```
**Engineering features demonstrated:**
- 📐 **Precise angle calculations** for optimal viewing/ergonomics
- 🔧 **Engineering tolerances** for 3D printing requirements
- 📱 **Multi-device compatibility** through parameterization
- 🎯 **Functional features** like cable management and stability
- 🏗️ **Modular design** with reusable components
- 📊 **Automatic layout algorithms** for optimal space usage
### Running Examples
To run any of these examples:
```bash
bun run ./examples/your-example.ts
```
This will generate OpenSCAD files that you can open and render in OpenSCAD, plus STL files ready for 3D printing.
## Acknowledgements
This project was inspired by many other projects: [farrellm/scad-clj](https://github.com/farrellm/scad-clj), [OpenJSCAD.org](https://openjscad.org/), [tasn/scadjs](https://github.com/tasn/scadjs) and more... And of course it would not even exist without [OpenSCAD](https://www.openscad.org) itself.
## License
This project is open source and available under the [MIT License](LICENSE).