Complete API reference and guide for using Nucleation in JavaScript and TypeScript (via WebAssembly).
npm install nucleationimport init, { SchematicWrapper } from "nucleation";
// Initialize WASM module
await init();
// Create schematic
const schematic = new SchematicWrapper();
schematic.set_block(0, 0, 0, "minecraft:stone");
// Save as litematic
const bytes = schematic.to_litematic();
// Download or save bytes...- Installation & Setup
- Core API
- Loading and Saving
- Block Operations
- Region Operations
- Block Entities
- SchematicBuilder
- Simulation
- TypedCircuitExecutor
- DefinitionRegion
- Procedural Building
- 3D Mesh Generation
npm install nucleationimport init, { SchematicWrapper } from "nucleation";
// Auto-detects environment and loads WASM
await init();
const schematic = new SchematicWrapper();<script type="module">
import init, {
SchematicWrapper,
} from "https://cdn.jsdelivr.net/npm/nucleation@latest/nucleation-cdn-loader.js";
await init(); // Automatically resolves WASM path
const schematic = new SchematicWrapper();
</script>import init, { SchematicWrapper } from "nucleation";
const wasmBytes = await fetch("/path/to/nucleation_bg.wasm").then((r) =>
r.arrayBuffer()
);
await init(wasmBytes);Main class for working with schematics.
class SchematicWrapper {
constructor(); // Creates empty schematic named "Default"
// Loading/Saving
from_data(bytes: Uint8Array): void;
from_litematic(bytes: Uint8Array): void;
from_schematic(bytes: Uint8Array): void;
to_litematic(): Uint8Array;
to_schematic(): Uint8Array;
toSnapshot(): Uint8Array;
fromSnapshot(bytes: Uint8Array): void;
// Format Support
static get_supported_import_formats(): string[];
static get_supported_export_formats(): string[];
static get_format_versions(format: string): string[];
static get_default_format_version(format: string): string | undefined;
save_as(format: string, version?: string): Uint8Array;
// Block operations
set_block(x: number, y: number, z: number, blockName: string): void;
set_block_with_properties(
x: number,
y: number,
z: number,
blockName: string,
properties: object
): void;
set_block_from_string(
x: number,
y: number,
z: number,
blockString: string
): void;
get_block(x: number, y: number, z: number): string | null;
get_block_with_properties(
x: number,
y: number,
z: number
): BlockStateWrapper | null;
// Block entities
get_block_entity(x: number, y: number, z: number): object | null;
get_all_block_entities(): Array<object>;
// Region operations
copy_region(
sourceRegion: string,
minX: number,
minY: number,
minZ: number,
maxX: number,
maxY: number,
maxZ: number,
targetX: number,
targetY: number,
targetZ: number,
excludedBlocks: string[]
): void;
// Information
get_dimensions(): [number, number, number];
get_block_count(): number;
get_volume(): number;
get_region_names(): string[];
debug_info(): string;
print_schematic(): string;
// Iteration
blocks(): Array<{
x: number;
y: number;
z: number;
name: string;
properties: object;
}>;
chunks(width: number, height: number, length: number): Array<any>;
chunks_with_strategy(
width: number,
height: number,
length: number,
strategy: string,
cx?: number,
cy?: number,
cz?: number
): Array<any>;
get_chunk_blocks(
offsetX: number,
offsetY: number,
offsetZ: number,
width: number,
height: number,
length: number
): Array<any>;
// Simulation (requires simulation feature)
create_simulation_world(): MchprsWorldWrapper;
create_simulation_world_with_options(
options: SimulationOptionsWrapper
): MchprsWorldWrapper;
}Represents a block with properties.
class BlockStateWrapper {
constructor(name: string);
with_property(key: string, value: string): void; // Mutates in place
name(): string;
properties(): object;
}// Browser
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener("change", async (e) => {
const file = e.target.files[0];
const bytes = new Uint8Array(await file.arrayBuffer());
const schematic = new SchematicWrapper();
schematic.from_data(bytes); // Auto-detects format
console.log(schematic.get_dimensions());
});
// Node.js
import { readFileSync } from "fs";
const bytes = new Uint8Array(readFileSync("input.litematic"));
const schematic = new SchematicWrapper();
schematic.from_litematic(bytes);
// Check supported formats
console.log(SchematicWrapper.get_supported_import_formats());
// ["litematic", "schematic", "mcstructure"]// Browser - Download
const bytes = schematic.to_litematic();
const blob = new Blob([bytes], { type: "application/octet-stream" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "output.litematic";
a.click();
URL.revokeObjectURL(url);
// Node.js
import { writeFileSync } from "fs";
const bytes = schematic.to_litematic();
writeFileSync("output.litematic", bytes);
// Check export formats
console.log(SchematicWrapper.get_supported_export_formats());
// ["litematic", "schematic", "mcstructure"]
// Check versions
console.log(SchematicWrapper.get_format_versions("schematic"));
// ["v1", "v2", "v3"]
console.log(SchematicWrapper.get_default_format_version("schematic"));
// "v3"
// Save with specific format and version
const schemBytes = schematic.save_as("schematic", "v2");
writeFileSync("output.v2.schem", schemBytes);The snapshot format is a fast binary serialization for caching and worker transfer. Much faster than standard formats but not compatible with Minecraft.
// Serialize to snapshot bytes
const snapshotData = schematic.toSnapshot();
// Load from snapshot (e.g., received from a Web Worker)
const schematic2 = new SchematicWrapper();
schematic2.fromSnapshot(snapshotData);// Simple block
schematic.set_block(0, 0, 0, "minecraft:stone");
// Block with properties (object)
schematic.set_block_with_properties(0, 1, 0, "minecraft:lever", {
facing: "east",
powered: "false",
});
// Block from string (bracket notation)
schematic.set_block_from_string(
1,
1,
0,
"minecraft:redstone_wire[power=15,north=side,south=side]"
);
// Using BlockStateWrapper
const block = new BlockStateWrapper("minecraft:repeater");
block.with_property("facing", "east");
block.with_property("delay", "2");
// Note: BlockStateWrapper is mainly for reading, use set_block_with_properties for setting// Get block name only
const blockName = schematic.get_block(0, 0, 0);
if (blockName) {
console.log(`Block: ${blockName}`);
}
// Get block with properties
const blockState = schematic.get_block_with_properties(0, 1, 0);
if (blockState) {
console.log(`Block: ${blockState.name()}`);
console.log(`Properties:`, blockState.properties());
}// Get all blocks
const allBlocks = schematic.blocks();
for (const block of allBlocks) {
console.log(`(${block.x}, ${block.y}, ${block.z}) = ${block.name}`);
console.log(`Properties:`, block.properties);
}
// Filter non-air blocks
const nonAirBlocks = allBlocks.filter((b) => !b.name.includes("air"));
// Count block types
const blockCounts = new Map();
for (const block of allBlocks) {
const count = blockCounts.get(block.name) || 0;
blockCounts.set(block.name, count + 1);
}// Get chunks (bottom-up order)
const chunks = schematic.chunks(16, 16, 16);
for (const chunk of chunks) {
console.log(
`Chunk at (${chunk.offset_x}, ${chunk.offset_y}, ${chunk.offset_z})`
);
console.log(`Blocks:`, chunk.blocks);
}
// Get chunks with strategy
const strategies = [
"distance_to_camera",
"top_down",
"bottom_up",
"center_outward",
"random",
];
const chunks = schematic.chunks_with_strategy(
16,
16,
16,
"distance_to_camera",
0,
100,
0 // Camera position
);
// Get specific chunk
const chunkBlocks = schematic.get_chunk_blocks(0, 0, 0, 16, 16, 16);// Copy a region
schematic.copy_region(
"Main", // Source region name
0,
0,
0, // Min coordinates
10,
10,
10, // Max coordinates
20,
0,
0, // Target position
["minecraft:air"] // Excluded blocks
);// Get all region names
const regions = schematic.get_region_names();
console.log(`Regions:`, regions);
// Get dimensions
const [width, height, depth] = schematic.get_dimensions();
console.log(`Size: ${width}x${height}x${depth}`);
// Get block/volume counts
console.log(`Blocks: ${schematic.get_block_count()}`);
console.log(`Volume: ${schematic.get_volume()}`);// Set block with entity using string notation
schematic.set_block_from_string(
0,
1,
0,
"minecraft:barrel[facing=up]{signal=13}"
);
// Note: Direct block entity manipulation is limited in WASM
// Use bracket notation with {nbt} for most cases// Get single block entity
const entity = schematic.get_block_entity(0, 1, 0);
if (entity) {
console.log(`Entity:`, entity);
// Entity is a plain JavaScript object with NBT data
}
// Get all block entities
const allEntities = schematic.get_all_block_entities();
for (const entity of allEntities) {
console.log(
`Entity at (${entity.x}, ${entity.y}, ${entity.z}):`,
entity.data
);
}Build schematics programmatically with ASCII art and compositional design.
See SchematicBuilder Guide for complete documentation.
import init, { SchematicBuilder } from "nucleation";
await init();
const circuit = SchematicBuilder.new()
.from_template(
`
# Base layer
ccc
ccc
# Logic layer
─→─
│█│
`
)
.build();// Build basic gates
const andGate = createAndGate();
const xorGate = createXorGate();
// Compose into larger circuit
const halfAdder = SchematicBuilder.new()
.map_schematic("A", andGate)
.map_schematic("X", xorGate)
.layers([["AX"]])
.build();Simulate redstone circuits in real-time.
import init, { SchematicWrapper } from "nucleation";
await init();
const schematic = new SchematicWrapper();
// Build circuit...
schematic.set_block_from_string(
0,
1,
0,
"minecraft:lever[facing=north,powered=false]"
);
schematic.set_block_from_string(5, 1, 0, "minecraft:redstone_lamp[lit=false]");
// ... add redstone wiring ...
// Create simulation world
const world = schematic.create_simulation_world();
// Toggle lever
world.on_use_block(0, 1, 0);
// Run simulation
world.tick(10);
world.flush();
// Check if lamp is lit
const isLit = world.is_lit(5, 1, 0);
console.log(`Lamp is lit: ${isLit}`);import { SimulationOptionsWrapper } from "nucleation";
// Configure custom IO positions
const options = new SimulationOptionsWrapper();
options.addCustomIo(0, 1, 0); // Input position
options.addCustomIo(10, 1, 0); // Output position
const world = schematic.create_simulation_world_with_options(options);
// Inject custom signal strength (0-15)
world.setSignalStrength(0, 1, 0, 15); // Max power
world.tick(5);
world.flush();
// Read signal strength
const outputSignal = world.getSignalStrength(10, 1, 0);
console.log(`Output signal: ${outputSignal}`);// Set multiple signals at once
const positions = [
[0, 1, 0],
[0, 1, 2],
[0, 1, 4],
];
const strengths = [15, 0, 15];
for (let i = 0; i < positions.length; i++) {
const [x, y, z] = positions[i];
world.setSignalStrength(x, y, z, strengths[i]);
}
world.tick(10);
world.flush();
// Read multiple signals
const outputs = positions.map(([x, y, z]) => world.getSignalStrength(x, y, z));High-level API for circuit simulation with typed inputs/outputs.
See TypedCircuitExecutor Guide for complete documentation.
import {
TypedCircuitExecutor,
IoType,
LayoutFunction,
Value,
} from "nucleation";
// Define IO mappings
const inputs = new Map([
[
"a",
{
io_type: IoType.Bool,
layout: LayoutFunction.OneToOne,
positions: [[0, 1, 0]],
},
],
[
"b",
{
io_type: IoType.Bool,
layout: LayoutFunction.OneToOne,
positions: [[0, 1, 2]],
},
],
]);
const outputs = new Map([
[
"result",
{
io_type: IoType.Bool,
layout: LayoutFunction.OneToOne,
positions: [[10, 1, 0]],
},
],
]);
// Create executor
const executor = new TypedCircuitExecutor(world, inputs, outputs);
// Execute with typed values
const inputValues = new Map([
["a", Value.Bool(true)],
["b", Value.Bool(true)],
]);
const result = executor.execute(inputValues, {
mode: "fixed_ticks",
ticks: 100,
});
// Get typed output
const output = result.outputs.get("result");
console.log(`Result: ${output}`); // Value.Bool(true)// Block state
interface BlockState {
name: string;
properties: Record<string, string>;
}
// Block with position
interface PositionedBlock {
x: number;
y: number;
z: number;
name: string;
properties: Record<string, string>;
}
// Chunk
interface Chunk {
offset_x: number;
offset_y: number;
offset_z: number;
blocks: PositionedBlock[];
}
// Execution mode
type ExecutionMode =
| { mode: "fixed_ticks"; ticks: number }
| { mode: "until_condition"; output: string; condition: any; timeout: number }
| { mode: "until_stable"; stable_ticks: number; timeout: number }
| { mode: "until_change"; timeout: number };function downloadSchematic(schematic: SchematicWrapper, filename: string) {
const bytes = schematic.to_litematic();
const blob = new Blob([bytes], { type: "application/octet-stream" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}async function uploadSchematic(file: File): Promise<SchematicWrapper> {
const bytes = new Uint8Array(await file.arrayBuffer());
const schematic = new SchematicWrapper();
schematic.from_data(bytes);
return schematic;
}
// Usage
const input = document.querySelector('input[type="file"]');
input.addEventListener("change", async (e) => {
const schematic = await uploadSchematic(e.target.files[0]);
console.log(schematic.get_dimensions());
});async function buildAndTestCircuit() {
await init();
const schematic = new SchematicWrapper();
// Build AND gate
schematic.set_block(0, 0, 0, "minecraft:stone");
schematic.set_block_from_string(
0,
1,
0,
"minecraft:lever[facing=north,powered=false]"
);
// ... build circuit ...
// Test simulation
const world = schematic.create_simulation_world();
world.on_use_block(0, 1, 0); // Toggle input
world.tick(10);
world.flush();
const output = world.is_lit(10, 1, 0);
console.log(`Test passed: ${output === true}`);
}Advanced region manipulation for defining circuit IO areas.
See Circuit API Guide for complete documentation.
import { DefinitionRegionWrapper, BlockPosition } from "nucleation";
// Create empty region
const region = new DefinitionRegionWrapper();
// Add bounding box
region.addBounds(new BlockPosition(0, 1, 0), new BlockPosition(7, 1, 0));
// Add single point
region.addPoint(10, 1, 0);
// Create from bounds directly
const region2 = DefinitionRegionWrapper.fromBounds(
new BlockPosition(0, 0, 0),
new BlockPosition(10, 10, 10)
);const regionA = new DefinitionRegionWrapper();
regionA.addBounds(new BlockPosition(0, 0, 0), new BlockPosition(5, 0, 0));
const regionB = new DefinitionRegionWrapper();
regionB.addBounds(new BlockPosition(3, 0, 0), new BlockPosition(8, 0, 0));
// Mutating operations (modify in-place)
regionA.subtract(regionB); // Remove B's points from A
regionA.intersect(regionB); // Keep only common points
regionA.unionInto(regionB); // Add B's points to A
// Immutable operations (return new region)
const diff = regionA.subtracted(regionB);
const common = regionA.intersected(regionB);
const combined = regionA.union(regionB);const region = new DefinitionRegionWrapper();
region.addBounds(new BlockPosition(0, 0, 0), new BlockPosition(10, 10, 10));
// Move region
region.shift(100, 50, 200);
// Expand outward
region.expand(2, 2, 2);
// Contract inward
region.contract(1);
// Get bounds
const bounds = region.getBounds();
// { min: [x, y, z], max: [x, y, z] } or null// Check if all points are connected (6-connectivity)
const isConnected = region.isContiguous();
// Count separate islands
const componentCount = region.connectedComponents();const schematic = new SchematicWrapper();
// ... add blocks ...
// Filter by block name
const lamps = region.filterByBlock(schematic, "redstone_lamp");
// Filter by properties
const litLamps = region.filterByProperties(schematic, { lit: "true" });// Get all positions (in add order)
const positions = region.positions(); // [[x,y,z], ...]
// Get positions in deterministic Y→X→Z order (for bit assignment)
const sortedPositions = region.positionsSorted();// ⚠️ IMPORTANT: Free WASM objects when done
const region = new DefinitionRegionWrapper();
// ... use region ...
region.free(); // Required to prevent memory leaksFluent API for creating TypedCircuitExecutor instances.
See Circuit API Guide for complete documentation.
import {
CircuitBuilderWrapper,
DefinitionRegionWrapper,
IoTypeWrapper,
BlockPosition,
} from "nucleation";
const schematic = new SchematicWrapper();
// ... build your circuit ...
// Define IO regions
const inputRegion = new DefinitionRegionWrapper();
inputRegion.addBounds(new BlockPosition(0, 1, 0), new BlockPosition(7, 1, 0));
const outputRegion = new DefinitionRegionWrapper();
outputRegion.addBounds(
new BlockPosition(0, 1, 20),
new BlockPosition(7, 1, 20)
);
// Build executor with fluent API
const executor = new CircuitBuilderWrapper(schematic)
.withInputAuto("data_in", IoTypeWrapper.unsignedInt(8), inputRegion)
.withOutputAuto("data_out", IoTypeWrapper.unsignedInt(8), outputRegion)
.withStateMode("stateful")
.buildValidated();
// Clean up regions (executor clones them)
inputRegion.free();
outputRegion.free();// Create from sign annotations in schematic
const builder = CircuitBuilderWrapper.fromInsign(schematic);
const executor = builder.build();const builder = new CircuitBuilderWrapper(schematic)
.withInputAuto("a", IoTypeWrapper.unsignedInt(8), regionA)
.withOutputAuto("out", IoTypeWrapper.unsignedInt(8), regionOut);
// Explicit validation (throws on error)
builder.validate();
// Or validate during build
const executor = builder.buildValidated();// Reset before each execute (default)
builder.withStateMode("stateless");
// Preserve state between executes
builder.withStateMode("stateful");
// Manual control (use tick/flush)
builder.withStateMode("manual");const executor = builder.withStateMode("manual").build();
// Set inputs individually
executor.setInput("a", ValueWrapper.fromU32(5));
executor.setInput("b", ValueWrapper.fromU32(3));
executor.flush();
// Tick manually
for (let i = 0; i < 10; i++) {
executor.tick(1);
executor.flush();
const result = executor.readOutput("sum");
console.log(`Tick ${i}: ${result.toJs()}`);
}// See exactly which block maps to which bit
const layoutInfo = executor.getLayoutInfo();
console.log("Inputs:");
for (const [name, info] of Object.entries(layoutInfo.inputs)) {
console.log(` ${name}: ${info.ioType} (${info.bitCount} bits)`);
info.positions.forEach((pos, bit) => {
console.log(` Bit ${bit}: [${pos.join(", ")}]`);
});
}Generate structures procedurally using geometric shapes and brushes.
import {
ShapeWrapper,
BrushWrapper,
WasmBuildingTool,
SchematicWrapper
} from "nucleation";
const schematic = new SchematicWrapper();
// Create a sphere shape
const sphere = ShapeWrapper.sphere(
0, 0, 0, // Center (x, y, z)
10.0 // Radius
);
// Create a gradient brush (Red -> Blue)
const brush = BrushWrapper.linear_gradient(
0, 0, 0, 255, 0, 0, // Start: Pos(0,0,0), Red(255,0,0)
10, 0, 0, 0, 0, 255, // End: Pos(10,0,0), Blue(0,0,255)
1, // 1 = Oklab interpolation (smoother), 0 = RGB
["wool"] // Optional filter: only use wool blocks
);
// Apply brush to shape
WasmBuildingTool.fill(schematic, sphere, brush);For simple tasks, you can use the direct methods on SchematicWrapper:
// Fill a cuboid region with a solid block
schematic.fillCuboid(
0, 0, 0, // Min [x, y, z]
10, 5, 10, // Max [x, y, z]
"minecraft:red_concrete"
);
// Fill a sphere with a solid block
schematic.fillSphere(
0, 0, 0, // Center [x, y, z]
10.0, // Radius
"minecraft:blue_wool"
);// Solid block
const solid = BrushWrapper.solid("minecraft:stone");
// Solid color (matches closest block)
const color = BrushWrapper.color(255, 128, 0, null); // Orange
// 4-Point Bilinear Gradient (Quad)
const bilinear = BrushWrapper.bilinear_gradient(
0, 0, 0, 10, 0, 0, 0, 10, 0, // Origin, U-end, V-end
255, 0, 0, // Origin Color (Red)
0, 0, 255, // U-end Color (Blue)
0, 255, 0, // V-end Color (Green)
255, 255, 0, // Opposite Color (Yellow)
1, // Oklab interpolation
null // No filter
);
// Point Cloud Gradient (Arbitrary points, IDW)
const points = BrushWrapper.point_gradient(
// Positions [x1, y1, z1, x2, y2, z2...]
[0, 0, 0, 10, 10, 10, 5, 5, 5],
// Colors [r1, g1, b1, r2, g2, b2...]
[255, 0, 0, 0, 0, 255, 0, 255, 0],
2.5, // Falloff (power), default 2.0
1, // Oklab
null
);Use PaletteManager to get blocks for your UI dropdowns.
import { PaletteManager } from "nucleation";
// Get all wool blocks
const woolBlocks = PaletteManager.getWoolBlocks();
// Get concrete blocks
const concreteBlocks = PaletteManager.getConcreteBlocks();
// Get custom mix (e.g., all wool + obsidian)
const customPalette = PaletteManager.getPaletteByKeywords(["wool", "obsidian"]);Generate 3D meshes from schematics using Minecraft resource packs.
import { ResourcePackWrapper, MeshConfigWrapper } from 'nucleation';
// Load from bytes (e.g., fetched ZIP file)
const response = await fetch('resourcepack.zip');
const data = new Uint8Array(await response.arrayBuffer());
const pack = new ResourcePackWrapper(data);
console.log(`Blockstates: ${pack.blockstateCount}`);
console.log(`Models: ${pack.modelCount}`);
console.log(`Textures: ${pack.textureCount}`);const config = new MeshConfigWrapper();
config.setCullHiddenFaces(true);
config.setAmbientOcclusion(true);
config.setAoIntensity(0.4);
config.setCullOccludedBlocks(true);
config.setGreedyMeshing(true);
// Generate GLB mesh
const result = schematic.toMesh(pack, config);
const glbData = result.glbData; // Uint8Array
console.log(`Vertices: ${result.vertexCount}, Triangles: ${result.triangleCount}`);
// Generate USDZ mesh
const usdzResult = schematic.toUsdz(pack, config);
// Generate raw mesh data
const raw = schematic.toRawMesh(pack, config);
const positions = raw.positionsFlat(); // Float32Array
const indices = raw.indices(); // Uint32Array// Per region
const multi = schematic.meshByRegion(pack, config);
const names = multi.getRegionNames();
names.forEach(name => {
const mesh = multi.getMesh(name);
// use mesh.glbData
});
// Per chunk
const chunks = schematic.meshByChunk(pack, config);
const coords = chunks.getChunkCoordinates(); // [[x,y,z], ...]
// Custom chunk size
const chunks32 = schematic.meshByChunkSize(pack, config, 32);const blockstates = pack.listBlockstates();
const models = pack.listModels();
const json = pack.getBlockstateJson("minecraft:stone");
const info = pack.getTextureInfo("minecraft:block/stone");
// { width, height, isAnimated, frameCount }
// Add custom entries
pack.addBlockstateJson("custom:block", jsonString);
pack.addModelJson("custom:block/my_block", modelJson);
pack.addTexture("custom:texture", 16, 16, rgbaBytes);