Write JSX in code, get procedural 3D meshes as GLB.
mesh-x is a small library for building 3D geometry programmatically and exporting
it to glTF/GLB. You describe a scene with JSX (or a plain builder API), and it
emits a standard .glb you can drop into Three.js, Blender, or any glTF viewer.
It covers primitives, boolean CSG, extrude/profile sweeps, a skeleton + skinning system, and procedural animations — enough to build characters, weapons, vehicles, and props entirely from code.
import { Scene, Box, Cylinder, writeGlb } from 'mesh-x'
function turret() {
return (
<Scene name="Turret">
<Cylinder radius={0.8} halfHeight={0.3} />
<Box position={[0, 0, 0.5]} hx={0.15} hy={0.6} hz={0.1} />
</Scene>
)
}
writeGlb(turret(), 'turret.glb')- JSX authoring —
<Scene>,<Box>,<Cylinder>,<Sphere>,<Extrude>,<Group>,<Empty>, etc. via thehfactory (configure your JSX runtime withjsxFactory: 'h'). - Primitives —
box,hollowBox,cylinder,zCylinder,sphere,frame,extrude,vent, plus profile sweeps (projectProfile). - CSG — boolean
subtract(a, b)(BSP-tree based) for cutting shapes. - Skinning —
Skin+Bonehierarchy with per-vertex joint weights for rigged, animatable characters. - Animations —
TranslationAnimation,RotationAnimation,MorphAnimation, plus a frame-based rig/FK/IK toolkit (buildRig,FKChain,AnimationFrame). - Geometry utilities —
GeometryBuilder(low-level),mirror, auto-smooth normals, winding verification, UV projectors (point/box/cylinder/sphere). - Z-up authoring — build in Blender-style Z-up; export converts to glTF Y-up.
npm install mesh-xThe JSX build pipeline uses esbuild, and the optional texture-atlas tooling
uses sharp — both are installed as optional dependencies.
import { Scene, box, GeometryBuilder, Mesh, writeGlb } from 'mesh-x'
const geo = new GeometryBuilder()
box(geo, { hx: 1, hy: 1, hz: 1 })
const scene = new Scene({ name: 'Cube' })
scene.add(new Mesh({ name: 'Cube', geometry: geo }))
writeGlb(scene, 'cube.glb')Author components as .jsx and transform them with the included loader hooks
(esbuild, jsxFactory: 'h'). A build pipeline is provided so consumer projects
ship a thin shim:
// build.js in your asset project
import { register } from 'node:module'
register('mesh-x/pipeline/jsx-hooks.js', import.meta.url)
import runBuild from 'mesh-x/pipeline/build.js'
import { join, dirname } from 'path'
import { fileURLToPath } from 'url'
const __dirname = dirname(fileURLToPath(import.meta.url))
await runBuild({
srcDir: join(__dirname, 'src'),
command: process.argv[2] || 'all',
})Then node build.js <assetName> discovers {Name}.meshx.json + {Name}.jsx
pairs and emits a .glb per asset.
mesh-x has two ways to give a mesh its surface, and you pick per project:
1. Color materials (default, no setup). Give a mesh a material — a color or
a PBR object — and export with no atlas. writeGlb emits one glTF material per
distinct material. Meshes sharing a material are merged, so N colors → N draw
calls (standard glTF behavior).
import { Scene, Mesh, GeometryBuilder, box, writeGlb } from 'mesh-x'
const geo = new GeometryBuilder()
box(geo, { hx: 0.5, hy: 0.5, hz: 0.5 })
const scene = new Scene({ name: 'Box' })
scene.add(new Mesh({ name: 'Box', geometry: geo, material: '#ff8800' }))
// or: material: { color: '#ff8800', metalness: 0.6, roughness: 0.3, emissive: '#330000' }
writeGlb(scene, 'box.glb') // no atlasDir → color mode2. Texture atlas (the optimization). For a game with a fixed palette, give
each mesh a swatch (a cell in a shared atlas) and pass atlasDir. Then every
mesh shares ONE texture and ONE material, so the whole model is a single draw
call regardless of color count.
scene.add(new Mesh({ name: 'A', geometry: geo, swatch: 'Body_Metal' }))
writeGlb(scene, 'out.glb', { atlasDir }) // atlas modeUse color materials to get going; reach for the atlas when you want the
single-draw-call win and have a palette. (A project generates its atlas — the
baseColor/orm/emission PNGs + atlas_palette.json — from its swatch list.)
Runnable scripts in examples/:
node examples/01-hello-box.js # simplest color export
node examples/02-colored-robot.js # multiple PBR materials, color mode
node examples/03-atlas-mode.js # shared-atlas single-draw-call modeAuthor in Z-up, -Y = forward (Blender convention). writeGlb converts to
glTF Y-up on export ((x, y, z) → (x, z, -y)), preserving winding.
npm testExtracted from a game asset pipeline and generalized for standalone use. Both material tiers (color + atlas) work; APIs may still shift before a 1.0.
MIT © Geptyro