bbmodel-viewer
v0.1.1
Published
Render and view Blockbench .bbmodel files (cubes + meshes, textures, bones) in the browser with three.js.
Maintainers
Readme
bbmodel-viewer
Render and view Blockbench .bbmodel files in the
browser with three.js. Drop a container element in,
point it at a file (URL, File, or parsed object) and get an orbit-controlled
3D preview — cubes and meshes, per-face UVs, textures, and the full bone
(outliner) hierarchy with correct pivots and rotations.
Built for exactly this kind of use case: previewing model files stored on a drive / CDN without opening Blockbench.
Features
- ✅ Cube elements with per-face UVs,
inflate, per-face UV rotation - ✅ Mesh (poly) elements (triangles + quads)
- ✅ Embedded textures (
data:sources) with crisp nearest-neighbour filtering - ✅ Full outliner hierarchy: groups/bones with pivots and
ZYXEuler rotation - ✅ Transparent textures (alpha cutout)
- ✅ Orbit / zoom / pan controls, optional auto-rotate and ground grid
- ✅ Auto camera framing, container resize handling, clean
dispose() - ✅ TypeScript types, ESM + CJS builds, three.js as a peer dependency
Install
npm install bbmodel-viewer threethree is a peer dependency (>=0.150.0) so your app controls the version.
Quick start
import { BBModelViewer } from "bbmodel-viewer";
const container = document.getElementById("viewer")!; // give it a size in CSS
const viewer = new BBModelViewer(container, {
background: "#20232a",
autoRotate: true,
});
await viewer.load("/models/creeper.bbmodel");The container should have a non-zero size (e.g. width: 100%; height: 480px).
The viewer fills it and follows resizes automatically.
From a file input or drag & drop (drive viewer)
input.addEventListener("change", (e) => {
const file = (e.target as HTMLInputElement).files?.[0];
if (file) viewer.loadFile(file);
});From an already-parsed object
const data = await fetch(url).then((r) => r.json());
await viewer.loadModel(data);Options
All options are optional.
| Option | Type | Default | Description |
| ----------------------- | --------------------------- | -------------- | ------------------------------------------------------- |
| background | string \| number \| null | "#20232a" | Scene background. null/"transparent" for see-through |
| grid | boolean | true | Ground grid aligned to the model base |
| controls | boolean | true | Orbit / zoom / pan |
| autoRotate | boolean | false | Spin the model |
| autoRotateSpeed | number | 1.0 | Spin speed |
| fov | number | 45 | Camera field of view (degrees) |
| ambientIntensity | number | 0.8 | Ambient light |
| directionalIntensity | number | 0.55 | Key light |
| doubleSided | boolean | true | Render both sides of every polygon |
| alphaTest | number | 0.02 | Alpha cutoff for transparent texels |
| showUntexturedFaces | boolean | false | Draw faces with no texture as flat color instead of hiding them |
| antialias | boolean | true | MSAA |
| pixelRatio | number | min(dpr, 2) | Device pixel ratio cap |
| onLoad | (model, object) => void | — | Fires after a model is added to the scene |
| onError | (error) => void | — | Fires on load/parse failure |
API
const viewer = new BBModelViewer(container, options);
await viewer.load(url, init?); // fetch + render a .bbmodel URL
await viewer.loadFile(file); // render a File/Blob
await viewer.loadModel(data); // render a parsed bbmodel object
viewer.frameModel(); // re-frame the camera on the model
viewer.resize(); // re-measure the container (also automatic)
viewer.clear(); // remove the model + free GPU memory
viewer.dispose(); // tear everything down
// Escape hatches for advanced use:
viewer.scene; // THREE.Scene
viewer.camera; // THREE.PerspectiveCamera
viewer.renderer; // THREE.WebGLRenderer
viewer.controls; // OrbitControls | undefinedLower-level building blocks
If you just want the geometry (e.g. to drop into your own scene):
import { MaterialLibrary, buildModelObject } from "bbmodel-viewer";
const materials = new MaterialLibrary(model, { alphaTest: 0.02, side: THREE.DoubleSide });
await materials.load();
const object3d = buildModelObject(model, materials); // THREE.Group
myScene.add(object3d);Coordinate system & conventions
- Units are Blockbench units (16 = one Minecraft block); the grid draws one cell per block.
- Rotations use Euler order
ZYX(matching Blockbench'sRz·Ry·Rx). - Per-face UVs are read as
[u_min, v_min, u_max, v_max]in texture pixels and normalized by each texture'suv_width/uv_height(falling back to the projectresolution). - Textures use
NearestFilterand no mipmaps for pixel-perfect art.
Notes & limitations
- Animations are not played yet (static pose only). The bone hierarchy is built, so this is a planned addition.
- Untextured faces are hidden (treated as transparent), matching how they
look in-game. Set
showUntexturedFaces: trueto draw them as flat color instead. Models with no textures at all are always drawn colored. box_uv: files store computed per-face UVs, which are used directly; a best-effort standard unwrap is used only if a face'suvis missing.- Cross-origin textures/URLs need appropriate CORS headers when loaded from a remote drive.
Development
npm install
npm run build # bundle to dist/ (ESM + CJS + d.ts)
npm run typecheck
npm run example # serve examples/ with ViteLicense
MIT
