JSPM

block-model-renderer

1.3.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 32
  • Score
    100M100P100Q78024F
  • License MIT

Minecraft block and item model rendering for Node.js

Package Exports

  • block-model-renderer

Readme

block-model-renderer

Minecraft block and item model rendering for Node.js. Render any block, item, or custom model JSON to an image, with full support for vanilla resource pack features.

npm version License: MIT

Features

  • Renders blocks, items, and custom models from a resource pack
  • Full vanilla model, blockstate, item-definition, and texture atlas support, with accurate lighting and tints
  • Animated textures with WebP and GIF output
  • Stack multiple resource packs with higher ones overriding lower ones, just like in Minecraft
  • Virtual asset handlers, serve files from memory, zips, HTTP, anywhere
  • Bundled overrides for block entities that Minecraft renders dynamically (signs, banners, chests, heads, and more)
  • PNG, JPEG, WebP, GIF, and AVIF output

Install

npm install block-model-renderer

Quick Start

import { renderBlock, renderItem, renderModel } from "block-model-renderer"

const assets = "C:/Users/ewanh/AppData/Roaming/.minecraft/resourcepacks/vanilla"

// Render a block by id
await renderBlock({
  id: "oak_log",
  assets,
  path: "oak_log.png"
})

// Render an item by id
await renderItem({
  id: "diamond_sword",
  assets,
  path: "diamond_sword.png"
})

// Render a custom model JSON
await renderModel({
  assets,
  model: {
    textures: { main: "block/stone" },
    elements: [{
      from: [0, 0, 0],
      to: [16, 16, 16],
      faces: {
        up:    { texture: "#main" },
        down:  { texture: "#main" },
        north: { texture: "#main" },
        south: { texture: "#main" },
        east:  { texture: "#main" },
        west:  { texture: "#main" }
      }
    }]
  },
  path: "custom.png"
})

API

renderBlock(args)

Renders a block by its id using the resource pack's blockstates and models.

Option Default Description
id The block id (e.g. "oak_log", "stone"). Namespace optional
assets [] The assets source, see Assets
blockstates {} Blockstate property values (e.g. { axis: "y", half: "top" })
display see below Display transform applied to the rendered block. See Display transforms
path If provided, saves the output to this file path. Format inferred from the extension
format Output format ("png", "jpeg", "webp", etc.). Overrides extension inference. See sharp's output docs for the full list of supported formats
output Options passed directly to the sharp format encoder (e.g. { quality: 85, mozjpeg: true } for JPEG). See sharp's output docs for all available options per format
width 256 Width of the rendered output image, in pixels
height 256 Height of the rendered output image, in pixels
animated false See Animated output
animatedWidth Inherits from width Width of the rendered output image when the output is animated, in pixels
animatedHeight Inherits from height Height of the rendered output image when the output is animated, in pixels
animatedOutput Options passed directly to the sharp encoder when the output is animated
maxAnimationFrames 4096 Maximum number of frames in animated output. If a model's textures can't all loop cleanly within this many frames, the loop is truncated and shorter textures may get cut short
ignoreAtlases false Render without enforcing texture atlas rules
version Minecraft version the assets are for. Enables era-appropriate behaviour (see Legacy Minecraft versions)
background transparent See Background

Default display:

{ rotation: [30, 225, 0], scale: [0.625, 0.625, 0.625], type: "fallback", display: "gui" }

renderItem(args)

Renders an item by id using its item definition.

Option Default Description
id The item id (e.g. "diamond_sword", "apple"). Namespace optional
assets [] The assets source
components {} Item components used by the item definition (e.g. { using_item: true } on a bow to show it drawn)
display { type: "fallback", display: "gui" } Display transform. See Display transforms
path, format, output, width, height, animated, animatedWidth, animatedHeight, animatedOutput, maxAnimationFrames, ignoreAtlases, version, background Same as renderBlock

renderModel(args)

Renders a custom model JSON directly, bypassing blockstate or item definition lookup.

Option Default Description
model {} A model JSON object (inherits from parent if specified, supports all vanilla model features)
assets [] The assets source
display Same as renderBlock Display transform. See Display transforms
path, format, output, width, height, animated, animatedWidth, animatedHeight, animatedOutput, maxAnimationFrames, ignoreAtlases, version, background Same as renderBlock

Return value

All three render functions return:

  • A Buffer when animated is false (default)
  • An object { buffer, format } when animated is truthy. The format field tells you what was actually produced. For example, animated: true produces "webp" if the model has animated textures, or "png" if it doesn't

Assets

The assets option tells the renderer where to find resource pack files. It can be any of:

  • A string, a path to a resource pack folder on disk
  • A virtual handler object, see Virtual handlers
  • An array of any combination of the above
  • Prepared assets, the return value of prepareAssets()

When given an array, entries are checked in order: the first entry that has a file wins (higher-priority packs override lower-priority ones). This lets you layer packs on top of vanilla, just like Minecraft does.

// Single pack
assets: "C:/Users/ewanh/AppData/Roaming/.minecraft/resourcepacks/vanilla"

// Multiple layers (first wins)
assets: [
  "C:/Users/ewanh/AppData/Roaming/.minecraft/resourcepacks/my-overrides",
  "C:/Users/ewanh/AppData/Roaming/.minecraft/resourcepacks/vanilla"
]

Virtual handlers

Any object with a read method can be used as an assets entry, letting you serve files from anywhere, a zip file, memory, an HTTP server, a database. No disk access required.

const zip = { /* ... loaded zip ... */ }

const handler = {
  async read(filePath) {
    const entry = zip.files[filePath]
    return entry ? await entry.buffer() : null
  },
  list(dir) {
    return zip.folders[dir] ?? []
  },
  filter(filePath) {
    return filePath.startsWith("assets/minecraft/recipes/")
  }
}

await renderBlock({ id: "stone", assets: handler, path: "out.png" })
Method Required Description
read(filePath) yes Return file contents (Buffer, Uint8Array, or string), or null / undefined if the file doesn't exist
list(dir) yes Return an array of filenames in the given directory
filter(filePath) no Return true to hide this file from lower-priority entries

prepareAssets(assets)

The renderer internally calls prepareAssets(assets) on each render to normalize the input and parse pack.mcmeta filters. If you're running many renders with the same assets, call it once yourself and pass the result for faster subsequent renders:

import { prepareAssets, renderBlock } from "block-model-renderer"

const assets = await prepareAssets([
  "C:/Users/ewanh/AppData/Roaming/.minecraft/resourcepacks/my-overrides",
  "C:/Users/ewanh/AppData/Roaming/.minecraft/resourcepacks/vanilla"
])

for (const id of ["stone", "dirt", "oak_log"]) {
  await renderBlock({ id, assets, path: `${id}.png` })
}

Block entity overrides

Minecraft renders some blocks dynamically at runtime using hardcoded geometry, with no corresponding model JSON in the vanilla resource pack. block-model-renderer ships with a bundled overrides pack that supplies model JSONs for these cases, so they render correctly without any setup from you.

The following categories are covered:

  • Banners
  • Bells
  • Chests
  • Conduits
  • Copper Golem Statues
  • Decorated Pots
  • Enchanting Table Books
  • End Portal & End Gateway
  • Mob Heads and Skulls
  • Shulker boxes
  • Signs
  • Water & Lava
  • Technical blocks (barrier, light, structure void, moving piston)

Limitation

The overrides pack is prepended to your assets array at the highest priority. Any blockstate or model covered by it will override whatever your own packs provide, the bundled version always wins. This is a renderer limitation, not a design choice. That said, since these blocks are rendered dynamically by vanilla, you're very unlikely to actually have modified these files.

Animated output

Minecraft textures with an accompanying .mcmeta animation block are supported out of the box. When the model uses animated textures, enable animated output with animated: true:

await renderBlock({
  id: "magma_block",
  assets,
  animated: true,
  path: "magma_block.webp"
})
Value Result
false Single-frame PNG (default). Renders frame 0 of any animated textures
true WebP if the model has animated textures, PNG otherwise
"webp" Same as true
"gif" GIF if the model has animated textures, PNG otherwise

Note: GIF doesn't handle semi-transparent pixels well. For textures like water or nether portals, stich with WebP.

Background

The background option sets the clear color behind the rendered model. Supports several formats:

// Transparent (default)
background: undefined

// Hex strings (3/4/6/8 digit)
background: "#ffffff"
background: "#ffffff80"

// rgb() / rgba()
background: "rgb(255, 255, 255)"
background: "rgba(255, 255, 255, 0.5)"

// Number (0xRRGGBB), fully opaque
background: 0xffffff

// Object
background: { r: 255, g: 255, b: 255, a: 0.5 }

Display transforms

The display option controls how the model is rotated, translated, and scaled before rendering. It takes one of three forms:

String - name of a context in the model's display block ("gui", "fixed", "ground", "firstperson_righthand", etc.). The renderer uses that context's transform from the model.

display: "firstperson_righthand"

Plain transform - an object with rotation, translation, and/or scale. Applied directly, ignoring anything the model defines.

display: { rotation: [30, 225, 0], scale: [0.625, 0.625, 0.625] }

Fallback transform - add type: "fallback" to a plain transform to first try the model's own display for a named context (display: "gui" by default), falling back to the object's own rotation/translation/scale if the model doesn't define that context.

// Use the model's "gui" transform if it defines one, otherwise use this one
display: {
  type: "fallback",
  rotation: [30, 225, 0],
  scale: [0.625, 0.625, 0.625]
}

// Use the model's "firstperson_righthand" transform if it defines one, otherwise use this one
display: {
  type: "fallback",
  display: "firstperson_righthand",
  rotation: [30, 225, 0],
  scale: [0.625, 0.625, 0.625]
}

Legacy Minecraft versions

The version option tells the renderer what Minecraft version the assets are for, so it can apply era-appropriate behaviour automatically. Older versions had quirks that modern ones don't, and this lets the renderer handle them transparently.

await renderBlock({
  id: "cactus",
  assets,
  version: "1.8.9",
  path: "cactus.png"
})

version accepts release-style version strings like "1.8", "1.16.5", or "26.1.2". Trailing segments are optional and treated as 0 (so "26" compares as "26.0.0"). Anything after a - is ignored, so snapshot, pre-release, and release-candidate suffixes work too: "1.21-pre1", "1.21-rc2", "26.1.2-snapshot-2".

Currently triggered behaviours:

  • Pre-1.13: prepends block/ to bare blockstate model refs (e.g. "model": "cactus" resolves to block/cactus, matching the implicit prefix the game used before the 1.13 flattening)
  • Pre-1.19.3: skips texture atlas membership rules (atlases didn't exist yet)

The option is accepted by every entry point (renderBlock, renderItem, renderModel, parseBlockstate, parseItemDefinition, loadModel) and is also propagated onto model objects as model.version, so manually constructed models can carry it through too.

Low-level API

For custom rendering pipelines, lower-level functions are available.

parseBlockstate(assets, id, args?)

Resolves a blockstate to a list of model references, picking variants or multipart cases based on the given property values.

Argument Description
assets The assets source
id The blockstate id
args.data Blockstate property values (e.g. { axis: "y", half: "top" })
args.ignoreAtlases Skip texture atlas membership rules for the returned models
args.version Minecraft version the assets are for. See Legacy Minecraft versions

Returns a list of model references, one per matching model.

parseItemDefinition(assets, id, args?)

Resolves an item definition to a list of model references, walking conditions, selects, and range dispatch based on the given properties.

Argument Description
assets The assets source
id The item id
args.data Item components used by the definition
args.display Display context, used by tint colour resolution
args.ignoreAtlases Skip texture atlas membership rules for the returned models
args.version Minecraft version the assets are for. See Legacy Minecraft versions

Returns a list of model references.

resolveModelData(assets, model)

Recursively resolves a model's parent chain, merging textures, elements, and other fields into a single flat model.

Argument Description
assets The assets source
model A model reference or inline model object

Returns the resolved model object.

makeModelScene()

Creates a fresh three.js scene and orthographic camera configured for block rendering.

Returns { scene, camera }.

The returned camera has a fitAspect = true flag that tells renderModelScene to adjust the camera's frustum to match the output aspect ratio (so non-square renders aren't squished). Set the same property on your own camera (camera.fitAspect = true) if you want the same behavior. Works for both OrthographicCamera and PerspectiveCamera. Without the flag, the camera is left exactly as you configured it.

loadModel(scene, assets, model, args?)

Builds a resolved model's geometry and materials as a three.js group. If scene is non-null, the group is also added to it; pass null to just get the group back without touching any scene.

Texture atlas rules are enforced here: if model.type is "block" or "item" and model.ignore_atlas_restrictions isn't set, the model is replaced with the missing-model placeholder when any face texture is in the wrong atlas. Set model.ignore_atlas_restrictions = true on the model to bypass.

Argument Description
scene The three.js scene to add the model to, or null to skip adding it
assets The assets source
model A resolved model (from resolveModelData)
args.display Display transform to apply to the model
args.version Minecraft version the assets are for. Sets model.version if not already present. See Legacy Minecraft versions

Returns a THREE.Group containing the loaded model.

renderModelScene(scene, camera, args?)

Renders a scene to an image buffer. Takes all the same output options as renderBlock / renderItem / renderModel.

Argument Description
scene The three.js scene to render
camera The camera to render from
args path, format, width, height, animated, animatedWidth, animatedHeight, maxAnimationFrames, background - same as renderBlock

Returns an image buffer, or { buffer, format } when args.animated is truthy.

readFile(path, assets, hint?)

Reads a file from the assets, walking entries in order and respecting filters.

Argument Description
path The file path, relative to the pack root (e.g. "assets/minecraft/textures/block/stone.png")
assets The assets source
hint If set, only look in the entry at this index. Use buf.hintIndex from a previous read to pair related lookups (like a PNG and its mcmeta)

Returns a Buffer with .path and .hintIndex fields, or undefined if not found.

listDirectory(dir, assets)

Lists files in a directory across all assets entries, merging results and respecting filters.

Argument Description
dir The directory path, relative to the pack root
assets The assets source

Returns a list of filenames.

import {
  makeModelScene,
  parseBlockstate,
  resolveModelData,
  loadModel,
  renderModelScene,
  prepareAssets
} from "block-model-renderer"

const assets = await prepareAssets("C:/Users/ewanh/AppData/Roaming/.minecraft/resourcepacks/vanilla")
const { scene, camera } = makeModelScene()
const models = await parseBlockstate(assets, "oak_log")

for (const model of models) {
  const resolved = await resolveModelData(assets, model)
  await loadModel(scene, assets, resolved)
}

const buffer = await renderModelScene(scene, camera, {
  path: "oak_log.png",
  width: 512,
  height: 512
})

isWaterloggable(id)

Checks whether the renderer recognises a block id as waterloggable. When true, passing { waterlogged: true } in the blockstate properties to renderBlock or parseBlockstate will add a water layer to the returned model. When false, the waterlogged property has no effect.

Argument Description
id The block id (e.g. "oak_stairs", "minecraft:lantern"). Namespace optional

Returns true if the block is waterloggable, false otherwise.

import { isWaterloggable } from "block-model-renderer"

isWaterloggable("oak_stairs") // true
isWaterloggable("stone")      // false

Custom extensions

In a few places the renderer accepts fields that aren't part of vanilla Minecraft's model or item format. They exist because the renderer needs a way to pass data from blockstates down into models, apply arbitrary tints, mark models as double-sided, and a few other things vanilla doesn't expose. They're primarily used internally, but they're fully usable by you too. You can set these fields on your own models and blockstates to get the same behaviour.

Model JSON

Field Example Description
x, y, z 90 Rotation angles (in degrees) applied to the whole model around each axis. Normally set by a blockstate variant, but can be set on a model directly too
uvlock true Keep face UVs aligned to world space when the model is rotated by x/y/z. Normally set by a blockstate variant
translation [8, 0, 8] [x, y, z] translation (in voxel units) applied to the whole model before rendering
scale [0.5, 0.5, 0.5] [x, y, z] scale applied to the whole model before rendering
transformation { translation: [0,0,0], scale: [1,1,1], left_rotation: [0,0,0,1], right_rotation: [0,0,0,1] } Translation, rotation, and scale applied to the whole model before rendering. Accepts the vanilla item-definition transformation form (translation/rotations/scale) or a flat 16-element matrix array.
ignore_rotations true Skip the display rotation for this model
double_sided true Render all faces from both sides
tints ["#FF0000", "#00FF00"] Array of hex colour strings. Faces with a tintindex look up their tint from this array
shader { type: "end_portal", layers: 15 } Apply the end portal / end gateway shader to the model
type "block", "item" Which texture atlas rules to enforce. Block-type models use only the manually provided display settings. Model-defined displays are ignored since they are meant to apply to items, not blocks
ignore_atlas_restrictions true Skip texture atlas membership checks for this model, letting it reference textures from any atlas
version "1.8.9" Minecraft version the model is for. Enables era-appropriate behaviour, see Legacy Minecraft versions

Blockstate JSON

Field Example Description
allow_invalid_rotations true Allow variant x/y/z rotation values that aren't multiples of 90

Item components

Extra fields that can be passed through the components arg on renderItem, or the data arg on parseItemDefinition. These aren't real Minecraft item components, they stand in for runtime context that the game would normally provide:

Field Example Description
team "red" Team colour context used by the team tint source
context_entity_type "pig" The entity type holding the item, used by context_entity_type selects
context_dimension "the_nether" The dimension the item is rendered in, used by context_dimension selects

Any future non-component select properties vanilla adds will work without renderer updates. The renderer looks up the property by name in components and checks whether its value equals any of the select's listed cases, so as long as the property is a plain string and you pass it in components, it resolves correctly.

License

MIT © Ewan Howell