nucleation 0.3.6

A high-performance Minecraft schematic parser and utility library
Documentation

Nucleation

A Minecraft schematic engine in Rust — load, build, simulate, mesh, and render schematics from seven languages.

Crates.io npm PyPI CI

This island is a 20-line JSON description. Every image in this README was built and rendered by nucleation itself — regenerate them all with tools/readme-media/generate.py.

Install

cargo add nucleation        # Rust
npm  install nucleation     # JavaScript / TypeScript (Node ≥ 18 or a bundler)
pip  install nucleation     # Python (CPython 3.12+)

Kotlin/JVM, PHP, C, and C++ ship as archives on Releasesquickstarts below.

Thirty seconds

from nucleation import Schematic

cube = Schematic.load_from_file("simple_cube.litematic")
d = cube.dimensions()
print((d.x, d.y, d.z))                        # (3, 3, 3)
print(cube.palette_json())                    # ["minecraft:air","minecraft:stone",...]

cube.set_block(1, 3, 1, "minecraft:glowstone")
cube.save_to_file_with_format("simple_cube.schem", "", "")   # format from extension

Same thing from JavaScript (the WASM build has no filesystem — bytes in, bytes out):

import { Schematic } from "nucleation";
import { readFileSync, writeFileSync } from "node:fs";

const cube = Schematic.fromData(readFileSync("simple_cube.litematic"));
cube.setBlock(1, 3, 1, "minecraft:glowstone");
writeFileSync("simple_cube.schem", Buffer.from(cube.toSchematicB64(), "base64"));

Every snippet in this README is executed in CI-adjacent tooling with its real output captured — the full set, with outputs, lives in docs/readme-snippets/.

Sculpt with shapes and brushes

Spheres, tori, cones, pyramids, bezier ribbons — combined with boolean ops, filled by brushes: solid, color-matched, gradient, or shaded. This torus is twelve rainbow gradient points snapped to the wool palette:

import colorsys, math
from nucleation import *

pos, cols = [], []
for i in range(12):
    a = 2 * math.pi * i / 12
    r, g, b = colorsys.hsv_to_rgb(i / 12, 0.95, 0.95)
    pos += [round(16 * math.cos(a)), 0, round(16 * math.sin(a))]
    cols += [int(r * 255), int(g * 255), int(b * 255)]

s = Schematic.create("torus")
brush = Brush.point_gradient(pos, bytes(cols), 4.0, InterpolationSpace.Oklab)
brush.set_palette(Palette.wool())
BuildingTool.fill(s, Shape.torus(0, 0, 0, 16, 6, 0, 1, 0), brush)

Terrain from a JSON description

Signed distance fields: primitives, smooth booleans, seeded noise — sampled into blocks through declarative material rules (surface shells, depth bands, gradients, flower scatter). Deterministic: same JSON, same island, every language.

from nucleation import Sdf

island = '''{"type": "displace", "amplitude": 3, "frequency": 0.1, "seed": 7,
             "child": {"type": "ellipsoid", "radii": [14, 8, 14]}}'''
rules = '''{"fill": [
  {"when": {"depthBelowSurface": {"min": 0, "max": 0}}, "block": "minecraft:grass_block"},
  {"when": {"depthBelowSurface": {"min": 1, "max": 3}}, "block": "minecraft:dirt"},
  {"block": "minecraft:stone"}]}'''

terrain = Sdf.schematic_from_sdf(island, rules, False, 0, 0, 0, 0, 0, 0)
# → 29×18×29, 6,927 blocks

Paint with palettes

Palettes turn colors into blocks: presets (wool, concrete, terracotta, wood, solid, …), tag/kind-filtered custom sets, lightness-sorted ramps, and gradient sampling — the value→block workflow behind heatmaps, fractals, and pixel art.

from nucleation import Palette
import json

ramp = json.loads(Palette.wool().gradient_ids_json(255, 80, 40, 60, 40, 180, 8))
# → orange_wool, red_wool, red_wool, red_wool, magenta_wool, purple_wool, purple_wool, blue_wool

Index a ramp by any value you like — say, Mandelbrot escape time:

for pz in range(128):
    for px in range(128):
        s.set_block(px, 0, pz, ramp[escape_iterations(px, pz)])

Edit without collateral damage

Masked fills touch only what you tell them to: fill_only_air builds around existing work, fill_replacing swaps listed blocks inside a shape — here, stone bricks weathering into mossy and cracked variants within a sphere:

BuildingTool.fill_replacing(
    castle, Shape.sphere(0, 4, 12, 9.0), weathered_brush,
    '["minecraft:stone_bricks"]')

Ask the block database

Block facts for all 1,196 Minecraft 26.2 blocks, extracted from Mojang's own data generator and the vanilla jars — kinds, base-block variant links, resolved tags, model-derived geometry, texture-derived colors:

from nucleation import Blocks
import json

json.loads(Blocks.get_json("minecraft:oak_stairs"))
# {"kind": "minecraft:stair", "base_block": "minecraft:oak_planks",
#  "tags": ["minecraft:mineable/axe", "minecraft:stairs", "minecraft:wooden_stairs"],
#  "full_cube": false, "color": [162, 131, 79],
#  "default_state": {"facing": "north", "half": "bottom", ...}, ...}

json.loads(Blocks.variants_of_json("minecraft:oak_planks"))
# [oak_planks, oak_button, oak_fence, oak_fence_gate, oak_pressure_plate, oak_slab, oak_stairs, ...]

The data refreshes itself: a weekly workflow watches Mojang's version manifest and opens a PR with regenerated data when a new Minecraft ships (how it works).

Simulate redstone

Headless circuit simulation via MCHPRS's redpiler — flip levers, tick, read lamps; or drive whole circuits through a typed executor with named inputs and outputs. Works in the browser (WASM) too:

import { Schematic, MchprsWorld } from "nucleation";

const circuit = Schematic.create("lamp_circuit");
for (let x = 0; x < 3; x++) circuit.setBlock(x, 0, 0, "minecraft:gray_concrete");
circuit.setBlockFromString(0, 1, 0, "minecraft:lever[facing=east,face=floor,powered=false]");
circuit.setBlockFromString(1, 1, 0, "minecraft:redstone_wire[power=0,east=side,west=side]");
circuit.setBlockFromString(2, 1, 0, "minecraft:redstone_lamp[lit=false]");

const world = MchprsWorld.create(circuit);
world.onUseBlock(0, 1, 0);   // flip the lever
world.tick(2);
world.flush();
world.isLit(2, 1, 0);        // → true

Diff, fingerprint, deduplicate

Structural diffs (added / removed / changed / swapped, translation-aware) and translation-invariant fingerprints for duplicate detection:

diff = Diff.compute(before, after, "exact")
diff.distance()                              # 1
Fingerprint.is_duplicate(before, after, "exact")   # False
Fingerprint.compute(before, "exact")         # "3fdae2c9855e4794b30f9895b0d31a2c"

Mesh and render

Any schematic → GLB/glTF or USDZ using any vanilla-format resource pack, and a headless GPU renderer for PNG previews — the one that drew every image on this page:

mesh = MeshResult.create(schem, ResourcePack.from_bytes(pack_zip), MeshConfig.create())
glb = base64.b64decode(mesh.glb_data_b64())   # 9,848 bytes, magic b'glTF'

cfg = RenderConfig.create(1200, 760)
cfg.set_isometric()
Renderer.render_to_file(schem, pack_zip, cfg, "island.png")

Formats and worlds

Schematics .litematic · Sponge .schem · WorldEdit .schematic · Bedrock .mcstructure · structure .nbt · .nusn (fast binary snapshot) — with auto-detection
Worlds import Anvil region files / world folders (optionally bounded), export schematics as playable worlds, stream chunk-by-chunk in constant memory
Versions convert blocks, block entities, items, and entities across Minecraft data versions (a Rust port of PaperMC's DataConverter), with loss reports
Bedrock Java ↔ Bedrock blockstate + block-entity translation via GeyserMC mappings (full 26.2 parity)

And more: auto-stack (detect a build's repeating lattice and restamp it bigger — a 4-bit adder into an 8-bit one), embedded scripting (generate schematics from Lua or JS scripts, palettes included), pluggable storage (memory / filesystem / S3 / Redis / Postgres behind one URI), and layer-art templates (schematics from ASCII art).

One API, seven languages

Every binding is generated from one annotated-Rust source of truth (src/bridge/) via Diplomat — committed, regenerated, and diffed in CI so they can never drift. Same types, same methods, per-language idioms:

Language Package Errors Naming
Rust nucleation crate (native API) Result snake_case
JavaScript npm install nucleation exceptions setBlock
Python pip install nucleation exceptions set_block
Kotlin/JVM Release JAR (JNA, 5 platforms bundled) kotlin.Result setBlock
PHP Release archive (php/ + FFI) DiplomatError setBlock
C Release archive (include/ + library) result structs Schematic_set_block
C++ Header-only over the C ABI diplomat::result set_block
import at.schem.nucleation.*

val schematic = Schematic.create("demo")
schematic.setBlock(1, 2, 3, "minecraft:stone").getOrThrow()
println(schematic.getBlockName(1, 2, 3).getOrThrow()) // "minecraft:stone"
<?php
require "php/index.php";
use Stencil\Lib;
use Stencil\Schematic;

Lib::init("/path/to/libnucleation.so");
$schematic = Schematic::create("demo");
$schematic->setBlock(1, 2, 3, "minecraft:stone");
echo $schematic->getBlockName(1, 2, 3); // "minecraft:stone"
#include "Schematic.h"

int main(void) {
    DiplomatStringView name = {"demo", 4};
    Schematic *s = Schematic_create(name);
    DiplomatStringView stone = {"minecraft:stone", 15};
    Schematic_set_block(s, 1, 2, 3, stone);
    Schematic_destroy(s);
    return 0;
}

What ships where: npm, PyPI, the release archives, and the JAR carry the full surface (schematics, formats, worlds, building, palettes, SDF, diff/fingerprint, meshing, simulation, rendering). The crates.io crate has meshing and rendering; simulation is git-only (nucleation = { git = "https://github.com/Schem-at/Nucleation", features = ["simulation"] }) because MCHPRS isn't on crates.io. The WASM build includes simulation and meshing; GPU rendering is native-only.

Documentation & development

cargo test                          # core suite
./tools/gen-bindings.sh             # regenerate bindings (diplomat-tool fork)
./examples/bridge_smoke/js/run.sh   # end-to-end smoke per language

CI regenerates bindings and fails on drift, exercises every built wheel and the assembled JAR before release, and runs the language smoke tests on every push.

License

MIT. See LICENSE.