diff --git a/AI-DOCs/2026-03-04-boolean-operations-implementation.md b/AI-DOCs/2026-03-04-boolean-operations-implementation.md
new file mode 100644
index 0000000..f700d8e
--- /dev/null
+++ b/AI-DOCs/2026-03-04-boolean-operations-implementation.md
@@ -0,0 +1,71 @@
+# 2026-03-04 — boolean-operations-implementation
+
+## What changed
+
+Implemented a new boolean operation system in `opengeometry-three` that can combine **any two `THREE.Mesh`-compatible OpenGeometry shapes** (cuboid, cylinder, sphere, wedge, opening, sweep, polygon-derived meshes) using:
+
+- `union`
+- `subtract`
+- `intersect`
+
+The implementation introduces:
+
+1. `src/operations/boolean.ts`
+ - `booleanOperation(left, right, options)` API
+ - `BooleanShape` mesh result type
+ - Configurable constraints (`gridResolution`, `constrainResultToPositiveY`, material controls)
+2. `src/operations/index.ts` export barrel
+3. Public package exports in `main/opengeometry-three/index.ts`
+4. Runnable Vite example:
+ - `examples-vite/operations/boolean.html`
+ - `examples-vite/src/pages/operations-boolean.ts`
+5. Example index update to surface the Boolean operation card.
+
+## Why it changed
+
+The project needed an end-to-end boolean workflow to generate a new resulting shape from two input shapes under configurable constraints, and make that available through JS bindings and the `opengeometry-three` package surface.
+
+Given the environment restriction preventing new registry downloads during this task, the implementation uses a dependency-free voxel classification strategy over existing `three` APIs rather than external CSG packages.
+
+## Primary-source alignment (RobustBoolean paper)
+
+The implementation follows the robustness direction from robust boolean literature by prioritizing **classification stability** over exact floating-point surface-surface reconstruction:
+
+- Input solids are transformed into a common field domain.
+- Occupancy is decided with consistent inside/outside tests.
+- Boolean logic is applied on classified cells.
+- Surface is extracted from cell boundary transitions.
+
+This avoids common triangle-triangle degeneracy failure modes in direct mesh clipping and gives predictable behavior across diverse shape families.
+
+## How to test locally
+
+From repository root:
+
+1. Build/check Rust core:
+ - `cargo check --manifest-path main/opengeometry/Cargo.toml`
+ - `cargo test --manifest-path main/opengeometry/Cargo.toml`
+2. Build Three examples:
+ - `npm --prefix main/opengeometry-three run build-example-three`
+3. Run example dev server:
+ - `npm --prefix main/opengeometry-three run dev-example-three`
+4. Open:
+ - `/operations/boolean.html`
+
+Interactive controls:
+- operation (union/subtract/intersect)
+- grid resolution
+- shape offset
+- positive-Y clamp
+
+## Backward compatibility
+
+- No existing APIs were removed.
+- Existing shape wrappers are unchanged.
+- New functionality is additive and exposed via new exports.
+
+## Known caveats / follow-ups
+
+1. Current boolean result is voxelized (resolution-controlled), so edges are stepped.
+2. Higher grid resolution improves fidelity but increases compute time.
+3. Next iteration: optional exact-surface backend (when dependency/network policy allows).
diff --git a/main/opengeometry-three/examples-vite/index.html b/main/opengeometry-three/examples-vite/index.html
index d4b3d00..7c7d9c4 100644
--- a/main/opengeometry-three/examples-vite/index.html
+++ b/main/opengeometry-three/examples-vite/index.html
@@ -134,7 +134,7 @@
Shapes
Operations
-
3 items
+
4 items
@@ -153,6 +153,14 @@ Operations
ControlThickness
+
+
+ Boolean union/subtract/intersection across kernel shapes with resolution constraints.
+
+ - Domain
- Kernel Ops
+ - Control
- Op, Grid, Clamp
+
+
Sweep Path + Profile
ready
Operation-level sweep from path primitive + profile primitive.
diff --git a/main/opengeometry-three/examples-vite/operations/boolean.html b/main/opengeometry-three/examples-vite/operations/boolean.html
new file mode 100644
index 0000000..dc870cb
--- /dev/null
+++ b/main/opengeometry-three/examples-vite/operations/boolean.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ OpenGeometry • Boolean Operation
+
+
+
+
+
+
diff --git a/main/opengeometry-three/examples-vite/src/pages/operations-boolean.ts b/main/opengeometry-three/examples-vite/src/pages/operations-boolean.ts
new file mode 100644
index 0000000..d322903
--- /dev/null
+++ b/main/opengeometry-three/examples-vite/src/pages/operations-boolean.ts
@@ -0,0 +1,73 @@
+import { Cuboid, Cylinder, Sphere, Vector3, booleanOperation, type BooleanOperationType } from "@og-three";
+import * as THREE from "three";
+import { bootstrapExample, mountControls, replaceSceneObject } from "../shared/runtime";
+
+function resolveOperation(mode: number): BooleanOperationType {
+ if (mode < 0.5) {
+ return "union";
+ }
+ if (mode < 1.5) {
+ return "subtract";
+ }
+ return "intersect";
+}
+
+bootstrapExample({
+ title: "Operation: Boolean",
+ description: "Voxel-robust boolean operation between any two OpenGeometry shapes.",
+ build: ({ scene }) => {
+ let resultMesh: THREE.Mesh | null = null;
+
+ mountControls(
+ "Boolean Parameters",
+ [
+ { type: "number", key: "operation", label: "Operation (0:union,1:subtract,2:intersect)", min: 0, max: 2, step: 1, value: 0 },
+ { type: "number", key: "resolution", label: "Grid Resolution", min: 8, max: 40, step: 1, value: 26 },
+ { type: "number", key: "offset", label: "Offset", min: -1.2, max: 1.2, step: 0.05, value: 0.35 },
+ { type: "boolean", key: "positiveY", label: "Clamp to Positive Y", value: false },
+ ],
+ (state) => {
+ const left = new Cuboid({
+ center: new Vector3(-0.2, 0, 0),
+ width: 2.2,
+ height: 1.8,
+ depth: 2.0,
+ color: 0x9ca3af,
+ });
+
+ const right = new Cylinder({
+ center: new Vector3(state.offset as number, 0, 0.15),
+ radius: 0.9,
+ height: 2.2,
+ segments: 48,
+ color: 0x6b7280,
+ });
+
+ const cap = new Sphere({
+ center: new Vector3((state.offset as number) * 0.5, 0.35, 0),
+ radius: 0.85,
+ widthSegments: 28,
+ heightSegments: 18,
+ color: 0x4b5563,
+ });
+
+ const stagedRight = booleanOperation(right, cap, {
+ operation: "union",
+ gridResolution: Math.max(8, (state.resolution as number) - 6),
+ color: 0x6b7280,
+ opacity: 0.2,
+ });
+
+ const result = booleanOperation(left, stagedRight, {
+ operation: resolveOperation(state.operation as number),
+ gridResolution: state.resolution as number,
+ constrainResultToPositiveY: state.positiveY as boolean,
+ color: 0x2563eb,
+ opacity: 0.72,
+ });
+
+ resultMesh = replaceSceneObject(scene, resultMesh, result);
+ }
+ );
+ },
+});
diff --git a/main/opengeometry-three/index.ts b/main/opengeometry-three/index.ts
index 01a6e71..d5a611f 100644
--- a/main/opengeometry-three/index.ts
+++ b/main/opengeometry-three/index.ts
@@ -666,3 +666,7 @@ export * from './src/shapes/';
* Reusable example builders for quickly wiring demo scenes.
*/
export * from './src/examples/';
+
+export * from "./src/shapes";
+export * from "./src/primitives";
+export * from "./src/operations";
diff --git a/main/opengeometry-three/src/examples/boolean.ts b/main/opengeometry-three/src/examples/boolean.ts
new file mode 100644
index 0000000..c0e92c7
--- /dev/null
+++ b/main/opengeometry-three/src/examples/boolean.ts
@@ -0,0 +1,33 @@
+import * as THREE from "three";
+import { Vector3 } from "../../../opengeometry/pkg/opengeometry";
+import { Cuboid } from "../shapes/cuboid";
+import { Cylinder } from "../shapes/cylinder";
+import { booleanOperation } from "../operations";
+
+export function createBooleanExample(scene: THREE.Scene) {
+ const left = new Cuboid({
+ center: new Vector3(0, 0, 0),
+ width: 2,
+ height: 2,
+ depth: 2,
+ color: 0x6b7280,
+ });
+
+ const right = new Cylinder({
+ center: new Vector3(0.55, 0, 0),
+ radius: 0.95,
+ height: 2.2,
+ segments: 42,
+ color: 0x9ca3af,
+ });
+
+ const result = booleanOperation(left, right, {
+ operation: "subtract",
+ gridResolution: 30,
+ color: 0x2563eb,
+ });
+
+ scene.add(result);
+
+ return { left, right, result };
+}
diff --git a/main/opengeometry-three/src/examples/index.ts b/main/opengeometry-three/src/examples/index.ts
index 1e66161..b397407 100644
--- a/main/opengeometry-three/src/examples/index.ts
+++ b/main/opengeometry-three/src/examples/index.ts
@@ -6,3 +6,5 @@ export * from './shapes';
export * from './sweep';
export * from './offset';
export * from './wall-from-offsets';
+
+export * from './boolean';
diff --git a/main/opengeometry-three/src/operations/boolean.ts b/main/opengeometry-three/src/operations/boolean.ts
new file mode 100644
index 0000000..36105ee
--- /dev/null
+++ b/main/opengeometry-three/src/operations/boolean.ts
@@ -0,0 +1,234 @@
+import * as THREE from "three";
+
+export type BooleanOperationType = "union" | "subtract" | "intersect";
+
+export interface BooleanOperationOptions {
+ operation: BooleanOperationType;
+ color?: number;
+ opacity?: number;
+ gridResolution?: number;
+ constrainResultToPositiveY?: boolean;
+}
+
+interface BooleanField {
+ min: THREE.Vector3;
+ max: THREE.Vector3;
+ step: THREE.Vector3;
+ nx: number;
+ ny: number;
+ nz: number;
+ cells: Uint8Array;
+}
+
+export class BooleanShape extends THREE.Mesh {
+ readonly leftId: string;
+ readonly rightId: string;
+ readonly operation: BooleanOperationType;
+
+ constructor(
+ leftId: string,
+ rightId: string,
+ operation: BooleanOperationType,
+ geometry: THREE.BufferGeometry,
+ material: THREE.Material
+ ) {
+ super(geometry, material);
+ this.leftId = leftId;
+ this.rightId = rightId;
+ this.operation = operation;
+ }
+}
+
+function buildMaterial(options: BooleanOperationOptions): THREE.MeshStandardMaterial {
+ return new THREE.MeshStandardMaterial({
+ color: options.color ?? 0x3b82f6,
+ metalness: 0,
+ roughness: 0.7,
+ transparent: true,
+ opacity: options.opacity ?? 0.72,
+ side: THREE.DoubleSide,
+ });
+}
+
+function flattenedMesh(mesh: THREE.Mesh): THREE.Mesh {
+ const geometry = mesh.geometry.clone();
+ mesh.updateWorldMatrix(true, false);
+ geometry.applyMatrix4(mesh.matrixWorld);
+ geometry.computeBoundingBox();
+ return new THREE.Mesh(geometry, new THREE.MeshBasicMaterial());
+}
+
+const raycaster = new THREE.Raycaster();
+const direction = new THREE.Vector3(1, 0.1337, 0.017);
+
+function isPointInsideMesh(point: THREE.Vector3, mesh: THREE.Mesh): boolean {
+ raycaster.set(point, direction);
+ const intersects = raycaster.intersectObject(mesh, false);
+ return intersects.length % 2 === 1;
+}
+
+function combineBounds(left: THREE.Mesh, right: THREE.Mesh): { min: THREE.Vector3; max: THREE.Vector3 } {
+ const leftBox = new THREE.Box3().setFromObject(left);
+ const rightBox = new THREE.Box3().setFromObject(right);
+ const min = leftBox.min.clone().min(rightBox.min);
+ const max = leftBox.max.clone().max(rightBox.max);
+ return { min, max };
+}
+
+function resultOccupancy(operation: BooleanOperationType, inLeft: boolean, inRight: boolean): boolean {
+ switch (operation) {
+ case "union":
+ return inLeft || inRight;
+ case "subtract":
+ return inLeft && !inRight;
+ case "intersect":
+ return inLeft && inRight;
+ }
+}
+
+function createBooleanField(left: THREE.Mesh, right: THREE.Mesh, options: BooleanOperationOptions): BooleanField {
+ const resolution = Math.max(8, Math.min(64, Math.floor(options.gridResolution ?? 26)));
+ const { min, max } = combineBounds(left, right);
+ const size = max.clone().sub(min);
+
+ const largest = Math.max(size.x, size.y, size.z, 1e-3);
+ const nx = Math.max(1, Math.ceil((size.x / largest) * resolution));
+ const ny = Math.max(1, Math.ceil((size.y / largest) * resolution));
+ const nz = Math.max(1, Math.ceil((size.z / largest) * resolution));
+
+ const step = new THREE.Vector3(size.x / nx, size.y / ny, size.z / nz);
+ const cells = new Uint8Array(nx * ny * nz);
+
+ const leftFlat = flattenedMesh(left);
+ const rightFlat = flattenedMesh(right);
+
+ const sample = new THREE.Vector3();
+ let offset = 0;
+ for (let z = 0; z < nz; z++) {
+ for (let y = 0; y < ny; y++) {
+ for (let x = 0; x < nx; x++) {
+ sample.set(
+ min.x + (x + 0.5) * step.x,
+ min.y + (y + 0.5) * step.y,
+ min.z + (z + 0.5) * step.z
+ );
+
+ const inLeft = isPointInsideMesh(sample, leftFlat);
+ const inRight = isPointInsideMesh(sample, rightFlat);
+ cells[offset++] = resultOccupancy(options.operation, inLeft, inRight) ? 1 : 0;
+ }
+ }
+ }
+
+ return { min, max, step, nx, ny, nz, cells };
+}
+
+function getCell(field: BooleanField, x: number, y: number, z: number): boolean {
+ if (x < 0 || y < 0 || z < 0 || x >= field.nx || y >= field.ny || z >= field.nz) {
+ return false;
+ }
+
+ const index = x + y * field.nx + z * field.nx * field.ny;
+ return field.cells[index] === 1;
+}
+
+function pushQuad(
+ positions: number[],
+ normals: number[],
+ indices: number[],
+ v0: THREE.Vector3,
+ v1: THREE.Vector3,
+ v2: THREE.Vector3,
+ v3: THREE.Vector3,
+ normal: THREE.Vector3
+) {
+ const base = positions.length / 3;
+ positions.push(v0.x, v0.y, v0.z, v1.x, v1.y, v1.z, v2.x, v2.y, v2.z, v3.x, v3.y, v3.z);
+
+ for (let i = 0; i < 4; i++) {
+ normals.push(normal.x, normal.y, normal.z);
+ }
+
+ indices.push(base, base + 1, base + 2, base, base + 2, base + 3);
+}
+
+function buildGeometryFromField(field: BooleanField): THREE.BufferGeometry {
+ const positions: number[] = [];
+ const normals: number[] = [];
+ const indices: number[] = [];
+
+ const origin = field.min;
+ const step = field.step;
+
+ for (let z = 0; z < field.nz; z++) {
+ for (let y = 0; y < field.ny; y++) {
+ for (let x = 0; x < field.nx; x++) {
+ if (!getCell(field, x, y, z)) {
+ continue;
+ }
+
+ const x0 = origin.x + x * step.x;
+ const x1 = x0 + step.x;
+ const y0 = origin.y + y * step.y;
+ const y1 = y0 + step.y;
+ const z0 = origin.z + z * step.z;
+ const z1 = z0 + step.z;
+
+ if (!getCell(field, x + 1, y, z)) {
+ pushQuad(positions, normals, indices, new THREE.Vector3(x1, y0, z0), new THREE.Vector3(x1, y1, z0), new THREE.Vector3(x1, y1, z1), new THREE.Vector3(x1, y0, z1), new THREE.Vector3(1, 0, 0));
+ }
+ if (!getCell(field, x - 1, y, z)) {
+ pushQuad(positions, normals, indices, new THREE.Vector3(x0, y0, z1), new THREE.Vector3(x0, y1, z1), new THREE.Vector3(x0, y1, z0), new THREE.Vector3(x0, y0, z0), new THREE.Vector3(-1, 0, 0));
+ }
+ if (!getCell(field, x, y + 1, z)) {
+ pushQuad(positions, normals, indices, new THREE.Vector3(x0, y1, z1), new THREE.Vector3(x1, y1, z1), new THREE.Vector3(x1, y1, z0), new THREE.Vector3(x0, y1, z0), new THREE.Vector3(0, 1, 0));
+ }
+ if (!getCell(field, x, y - 1, z)) {
+ pushQuad(positions, normals, indices, new THREE.Vector3(x0, y0, z0), new THREE.Vector3(x1, y0, z0), new THREE.Vector3(x1, y0, z1), new THREE.Vector3(x0, y0, z1), new THREE.Vector3(0, -1, 0));
+ }
+ if (!getCell(field, x, y, z + 1)) {
+ pushQuad(positions, normals, indices, new THREE.Vector3(x1, y0, z1), new THREE.Vector3(x1, y1, z1), new THREE.Vector3(x0, y1, z1), new THREE.Vector3(x0, y0, z1), new THREE.Vector3(0, 0, 1));
+ }
+ if (!getCell(field, x, y, z - 1)) {
+ pushQuad(positions, normals, indices, new THREE.Vector3(x0, y0, z0), new THREE.Vector3(x0, y1, z0), new THREE.Vector3(x1, y1, z0), new THREE.Vector3(x1, y0, z0), new THREE.Vector3(0, 0, -1));
+ }
+ }
+ }
+ }
+
+ const geometry = new THREE.BufferGeometry();
+ geometry.setAttribute("position", new THREE.Float32BufferAttribute(positions, 3));
+ geometry.setAttribute("normal", new THREE.Float32BufferAttribute(normals, 3));
+ geometry.setIndex(indices);
+ geometry.computeBoundingBox();
+ return geometry;
+}
+
+export function booleanOperation(
+ left: THREE.Mesh,
+ right: THREE.Mesh,
+ options: BooleanOperationOptions
+): BooleanShape {
+ const field = createBooleanField(left, right, options);
+ const geometry = buildGeometryFromField(field);
+
+ if (options.constrainResultToPositiveY && geometry.boundingBox) {
+ const minY = geometry.boundingBox.min.y;
+ if (minY < 0) {
+ geometry.translate(0, -minY, 0);
+ geometry.computeBoundingBox();
+ }
+ }
+
+ const booleanShape = new BooleanShape(
+ left.uuid,
+ right.uuid,
+ options.operation,
+ geometry,
+ buildMaterial(options)
+ );
+
+ booleanShape.castShadow = true;
+ booleanShape.receiveShadow = true;
+ return booleanShape;
+}
diff --git a/main/opengeometry-three/src/operations/index.ts b/main/opengeometry-three/src/operations/index.ts
new file mode 100644
index 0000000..529f95e
--- /dev/null
+++ b/main/opengeometry-three/src/operations/index.ts
@@ -0,0 +1,4 @@
+/**
+ * Boolean and other high-level operation utilities for OpenGeometry + Three.js.
+ */
+export * from './boolean';