High-performance voxel meshing library built in Rust, compiled to WebAssembly, designed for Three.js.
Greedy meshing with per-vertex ambient occlusion, zero allocations in the hot path, and cross-chunk boundary handling — all running at native speed in the browser.
- Greedy meshing — merges adjacent faces into larger quads, drastically reducing vertex count
- Ambient occlusion — per-vertex AO computed from neighboring block opacity with anisotropy-correct triangulation
- Cross-chunk boundaries — seamless meshing across chunk edges via neighbor border data
- Zero-alloc hot path — pre-allocated scratch buffers reused across mesh calls
- Three.js ready — outputs
Float32Array/Uint32Arraybuffers directly compatible withBufferGeometry
crates/urath-core/ Pure Rust. Meshing algorithms, chunk data, output buffers.
crates/urath-wasm/ Thin wasm-bindgen wrapper. JS ↔ WASM buffer marshalling.
packages/urath/ TypeScript npm package. WASM bindings + Three.js integration.
examples/demo/ Interactive Three.js demo with block placement.
# Build WASM
cd crates/urath-wasm && wasm-pack build --target web --out-dir ../../packages/urath/wasm
# Build TypeScript package
cd packages/urath && npm run buildimport init, {
WasmChunk,
WasmChunkNeighbors,
WasmGreedyMesher,
} from "urath/wasm/urath_wasm.js";
await init();
// Create a 32x32x32 chunk
const chunk = WasmChunk.new_default();
// Set some blocks (block ID 0 = air)
chunk.set_block(0, 0, 0, 1);
chunk.set_block(1, 0, 0, 1);
chunk.set_block(0, 1, 0, 2);
// Or batch set: [x, y, z, id, x, y, z, id, ...]
chunk.set_blocks(new Uint32Array([2, 0, 0, 1, 3, 0, 0, 1]));
// Mesh it
const mesher = new WasmGreedyMesher(32);
const result = mesher.mesh(chunk);
// Use with Three.js BufferGeometry
const geometry = new THREE.BufferGeometry();
geometry.setAttribute("position", new THREE.BufferAttribute(result.positions(), 3));
geometry.setAttribute("normal", new THREE.BufferAttribute(result.normals(), 3));
geometry.setAttribute("ao", new THREE.BufferAttribute(result.ao(), 1));
geometry.setIndex(new THREE.BufferAttribute(result.indices(), 1));For seamless meshing between adjacent chunks, provide neighbor border data:
const neighbors = new WasmChunkNeighbors(32);
// Faces: 0=+X, 1=-X, 2=+Y, 3=-Y, 4=+Z, 5=-Z
neighbors.set_neighbor(0, adjacentChunkPosX);
neighbors.set_neighbor(1, adjacentChunkNegX);
const result = mesher.mesh_with_neighbors(chunk, neighbors);The core crate can be used directly in Rust without WASM:
use urath_core::{Chunk, ChunkNeighbors, GreedyMesher, Mesher, MeshOutput};
let mut chunk = Chunk::new_default()?;
chunk.set(0, 0, 0, 1);
chunk.set(1, 0, 0, 1);
let neighbors = ChunkNeighbors::empty(chunk.size());
let mut output = MeshOutput::with_capacity(1024);
let mut mesher = GreedyMesher::new();
mesher.mesh(&chunk, &neighbors, &mut output)?;
// Reuse buffers for next chunk
output.clear();An interactive Three.js demo with block placement is included. Build the WASM package first, then serve the demo directory:
# Build WASM (if not already built)
cd crates/urath-wasm && wasm-pack build --target web --out-dir ../../packages/urath/wasm
# Serve the demo (any static HTTP server works)
npx serve examples/demoOpen the printed URL (usually http://localhost:3000) in your browser.
cd crates/urath-core && cargo benchBenchmarks cover empty, solid, surface, and noisy terrain chunk configurations.
cd crates/urath-core && cargo testMIT OR Apache-2.0