# minecraft-renderer

> The most Modular Minecraft world renderer with Three.js WebGL backend

Latest version **0.1.99** (published 2026-09-04) · MIT license · 0 weekly downloads

## Install

```sh
npm install minecraft-renderer
pnpm add minecraft-renderer
yarn add minecraft-renderer
bun add minecraft-renderer
```

## Health

**Score 70/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; has provenance; recently updated; high maintenance score.

Warnings: low downloads; large bundle; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.1.99 |
| Published | 2026-09-04 |
| First published | 2025-12-03 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | ^12.20.0 \|\| ^14.13.1 \|\| >=16.0.0 |
| Dependencies | 19 |
| Unpacked size | 17.3 MB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 7 |
| Maintainers | zardoy |
| Keywords | minecraft, renderer, 3d-engine, threejs, webgl, prismarine |

## Links

- npm: https://www.npmjs.com/package/minecraft-renderer
- Repository: https://github.com/zardoy/minecraft-renderer
- Homepage: https://github.com/zardoy/minecraft-renderer#readme
- Issues: https://github.com/zardoy/minecraft-renderer/issues
- npm.io page: https://npm.io/package/minecraft-renderer

## Dependencies (19)

- [vec3](https://npm.io/package/vec3.md) ^0.1.10
- [three](https://npm.io/package/three.md) 0.184.0
- [events](https://npm.io/package/events.md) ^3.3.0
- [valtio](https://npm.io/package/valtio.md) ^1.11.1
- [stats-gl](https://npm.io/package/stats-gl.md) ^1.0.5
- [stats.js](https://npm.io/package/stats.js.md) ^0.17.0
- [deepslate](https://npm.io/package/deepslate.md) ^0.24.0
- [mc-bridge](https://npm.io/package/mc-bridge.md) ^0.1.3
- [type-fest](https://npm.io/package/type-fest.md) ^5.3.0
- [skinview3d](https://npm.io/package/skinview3d.md) ^3.4.1
- [three-stdlib](https://npm.io/package/three-stdlib.md) ^2.36.1
- [@types/events](https://npm.io/package/@types/events.md) ^3.0.3
- [typed-emitter](https://npm.io/package/typed-emitter.md) ^2.1.0
- [prismarine-nbt](https://npm.io/package/prismarine-nbt.md) ^2.5.0
- [skinview-utils](https://npm.io/package/skinview-utils.md) ^0.7.1
- [prismarine-chat](https://npm.io/package/prismarine-chat.md) ^1.10.0
- [mineflayer-mouse](https://npm.io/package/mineflayer-mouse.md) ^0.1.24
- [@tweenjs/tween.js](https://npm.io/package/@tweenjs/tween.js.md) ^20.0.3
- [@xmcl/text-component](https://npm.io/package/@xmcl/text-component.md) ^2.1.3

## Alternatives

- [@oh-my-pi/pi-natives](https://npm.io/package/@oh-my-pi/pi-natives.md) — 51.8K weekly downloads
- [@capgo/capacitor-light-sensor](https://npm.io/package/@capgo/capacitor-light-sensor.md) — 3.0K weekly downloads
- [@heyhuynhgiabuu/pi-diff](https://npm.io/package/@heyhuynhgiabuu/pi-diff.md) — 492 weekly downloads
- [@lotsa/verdant-lang-asm](https://npm.io/package/@lotsa/verdant-lang-asm.md) — 38 weekly downloads
- [new-era-syntax](https://npm.io/package/new-era-syntax.md) — 20 weekly downloads

## Recent versions

- 0.1.99 (latest) — 2026-09-04
- 0.1.98 — 2026-09-04
- 0.1.97 — 2026-09-04
- 0.1.96 — 2026-08-03
- 0.1.95 — 2026-08-02
- 0.1.94 — 2026-08-01
- 0.1.93 — 2026-07-27
- 0.1.92 — 2026-07-12
- 0.1.91 — 2026-07-11
- 0.1.90 — 2026-07-10
- 0.1.89 — 2026-07-07
- 0.1.88 — 2026-07-07
- 0.1.87 — 2026-07-07
- 0.1.86 — 2026-07-06
- 0.1.85 — 2026-07-04
- … 85 more at https://npm.io/package/minecraft-renderer/versions

## README

# Minecraft Renderer

![Minecraft Renderer](./logo.webp)

One of the best Minecraft world rendererers implemented from scratch. Uses Three.js WebGL 2 backend. Designed for performance testing, experimentation, and integration into Minecraft clients or other use cases for game world display.

Features:

- 💡 Full-featured: hand, third-person view, entities, debug features and even more!
- ⚡️ Leverages all available WebGL 2 and WASM world meshing for the maximum performance
- 📦 Implemented from scratch; small bundle size and runtime footprint
- ⚙️ Easily customizable: modular architecture with Three.js API

## Implemented Features

- WASM mesher workers (default path) with legacy JS mesher fallback
- Instanced shader-cube rendering for full blocks (`GlobalBlockBuffer`, one GPU draw)
- Global legacy geometry buffer for models/stairs/slabs (merged indexed mesh, opaque + blend passes)
- Block and sky lighting, smooth lighting, and vanilla vs high-contrast (default) face shading (`vanillaLook`)
- Signs, banners, skulls, and other block-entity overlays
- Entities: players with skins & animations, mobs, items, armor, text/item display
- Day cycle, skybox, starfield, rain, fireworks
- Third-person camera, view bobbing, holding block / hand
- Optional off-thread graphics backend (render in a worker!)
- Smooth lighting (lighting data has to be provided)

### Browser support

**Requires WebGL 2.0.** WebGL 1 is not supported as a full-feature path (shader cubes and several block shaders need WebGL2).

| Browser              | Minimum version |
| -------------------- | --------------- |
| Chrome / Chromium    | 56+             |
| Firefox              | 51+             |
| Edge                 | 79+ (Chromium)  |
| Safari (macOS / iOS) | 15.3+           |
| Opera                | 43+             |

**Not supported:** Safari before 15.

Optional extensions (`WEBGL_multi_draw`, instanced base vertex) improve draw-call batching when present; the renderer falls back to capped multi-draw loops when they are missing.

## Architecture Overview

```
┌─────────────────────────────────────────────────────────────────┐
│                         AppViewer                                │
│  - Manages graphics backend lifecycle                            │
│  - Handles world view and player state                           │
│  - Coordinates between data and rendering                        │
└─────────────────────────────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────┐
│                     GraphicsBackend (Three.js)                   │
│  - WebGL rendering via Three.js                                  │
│  - Scene, camera, and lighting management                        │
│  - Mesher worker coordination                                    │
└─────────────────────────────────────────────────────────────────┘
                               │
          ┌────────────────────┼────────────────────┐
          ▼                    ▼                    ▼
┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐
│ DocumentRenderer │  │WorldGeometryHandler│ │    StarField     │
│ - Render loop    │  │ - Chunk meshes    │  │ - Night sky      │
│ - Canvas sizing  │  │ - GPU memory      │  │ - Twinkling      │
│ - FPS tracking   │  │ - Signs/banners   │  │   effect         │
└──────────────────┘  └──────────────────┘  └──────────────────┘
```

## Core Components

### WorldView (formerly WorldDataEmitter)

Manages chunk loading/unloading and emits world events to the renderer.

```typescript
import { WorldView } from 'minecraft-renderer'

// Create world view with a world provider
const worldView = new WorldView(worldProvider, renderDistance, startPosition)

// Initialize and start loading chunks
await worldView.init(playerPosition)

// Update position (loads/unloads chunks as needed)
await worldView.updatePosition(newPosition)

// Set block and emit update
worldView.setBlockStateId(position, stateId)
```

### AppViewer

Main application entry point for integrating the renderer.

```typescript
import { AppViewer, createGraphicsBackend } from 'minecraft-renderer'

const viewer = new AppViewer({
  config: {
    sceneBackground: 'lightblue',
    fpsLimit: 60
  },
  rendererConfig: {
    enableLighting: true,
    smoothLighting: true,
    showChunkBorders: false
  }
})

// Load backend
await viewer.loadBackend(createGraphicsBackend)

// Start rendering world
await viewer.startWorld(worldProvider, renderDistance)

// Update camera each frame
viewer.updateCamera(position, yaw, pitch)
```

### Settings flow (app integration)

Renderer-owned options live in `RENDERER_DEFAULT_OPTIONS` and `RENDERER_OPTIONS_META` (`src/graphicsBackend/rendererDefaultOptions.ts`).

1. **Defaults** — spread `RENDERER_DEFAULT_OPTIONS` into your app options store (e.g. valtio `options`).
2. **Migration** — call `migrateRendererOptions(saved)` when loading persisted settings (legacy mesher/GPU keys → current renderer option names).
3. **Settings UI** — merge `RENDERER_OPTIONS_META` into your options meta; layout can stay app-owned.
4. **Menu startup** — `startMenuBackground(menuBackgroundOptionsFromStorage(options))`.
5. **Runtime sync** — after `AppViewer` + backend init, call once:
   `subscribeRendererOptions(appViewer, options, { isSafari, isCypress, onRegisterFocusHandlers })`.
   This updates `inWorldRenderingConfig`, `appViewer.config` (FPS/stats), and live menu-background controls when `currentDisplay === 'menu'`.
6. **App-only** — keep `volume` and bot/world hooks in the client (`applyRendererEnableLighting`, `applyRendererWorldViewOptions`, weather).

| Change                                  | Live update                       | Reload required                        |
| --------------------------------------- | --------------------------------- | -------------------------------------- |
| Menu V2 scene / camera / speeds         | Yes (`backend.getMenuBackground`) | Mode switch needs restart              |
| `rendererMesher` (`wasm` / `legacy-js`) | Yes — recreates mesher workers    | Chunks reload (`requiresChunksReload`) |
| `rendererWorldPerformance`              | Yes — recreates mesher workers    | Chunks reload (`requiresChunksReload`) |
| Volume                                  | App `watchValue` only             | No                                     |

Sync runs on the **main thread** only; `inWorldRenderingConfig` uses existing valtio `__syncToWorker` for off-thread backends. Do not call `subscribeRendererOptions` from mesher workers.

## How Block Rendering Works

### 1. Chunk Data Flow

```
World Provider → WorldView → GraphicsBackend → Mesher Workers → Three.js Scene
```

1. **World Provider**: Provides chunk column data (prismarine-chunk format)
2. **WorldView**: Emits `loadChunk` events with serialized chunk data
3. **GraphicsBackend**: Receives events and dispatches to mesher workers
4. **Mesher Workers**: Generate geometry (positions, normals, UVs, colors)
5. **Three.js Scene**: Creates BufferGeometry meshes from worker output

### 2. Mesher Worker Communication

Workers receive:

- Block data (chunk JSON with block state IDs)
- Block models and textures atlas
- Lighting configuration

Workers produce:

- Float32Array of vertex positions (x, y, z per vertex)
- Float32Array of normals
- Float32Array of colors (vertex colors for lighting)
- Float32Array of UVs (texture coordinates)
- Uint16/32Array of indices

### 3. Geometry Structure

Each block face is a quad with 4 vertices and 6 indices:

```typescript
interface MesherGeometryOutput {
  positions: Float32Array // [x1,y1,z1, x2,y2,z2, ...]
  normals: Float32Array // [nx,ny,nz, ...]
  colors: Float32Array // [r,g,b, r,g,b, ...] (0-1 range, lighting)
  uvs: Float32Array // [u1,v1, u2,v2, ...] (texture atlas coords)
  indices: Uint32Array // [0,1,2, 2,3,0, ...] (triangles)
  sx
  sy
  sz: number // Section position offset
  blocksCount: number // Number of non-air blocks
  signs: Record<string, SignData>
  banners: Record<string, BannerData>
  heads: Record<string, HeadData>
}
```

### 4. Block Model Resolution

1. Block state ID → Block state properties
2. Block state properties → Blockstate JSON
3. Blockstate JSON → Model variants
4. Model JSON → Faces with texture references
5. Texture references → Atlas UV coordinates

### 5. Lighting Calculation

Lighting uses both block light and sky light:

```typescript
// Light level 0-15 for both block and sky light
const blockLight = chunk.getBlockLight(pos)
const skyLight = chunk.getSkyLight(pos)

// Combined light level
const light = Math.max(blockLight, Math.min(skyLight, skyLightCap))

// Light level to color multiplier
const brightness = lightLevelToBrightness[light]
// Applied as vertex color: [brightness, brightness, brightness]
```

### 6. Ambient Occlusion

Smooth lighting uses ambient occlusion based on neighboring blocks:

```typescript
// For each vertex, check 3 neighboring blocks
// AO value = (side1 + side2 + corner) / 3
// Applied as vertex color darkening
```

## Configuration

### WorldRendererConfig

```typescript
interface WorldRendererConfig {
  // Performance
  mesherWorkers: number // Number of worker threads (default: 4)
  addChunksBatchWaitTime: number // Batch delay for chunk loading (ms)
  _experimentalSmoothChunkLoading: boolean

  // Rendering
  enableLighting: boolean // Enable block/sky lighting
  smoothLighting: boolean // Enable ambient occlusion
  dayCycle: boolean // Enable time-based sky changes
  starfield: boolean // Enable star field at night
  fov: number // Camera field of view

  // Debug
  showChunkBorders: boolean // Show chunk boundary helpers
  enableDebugOverlay: boolean // Show advanced stats
  clipWorldBelowY: number | undefined // Don't render below Y level
}
```

## Memory Management

The renderer implements several memory optimizations:

1. **CPU Array Disposal**: After GPU upload, CPU-side typed arrays are nulled
2. **Texture Caching**: Signs and banners share textures via reference counting
3. **Section Tracking**: Memory usage is tracked per section for debugging

```typescript
// Get memory usage
const { bytes, readable } = worldGeometryHandler.getMemoryUsageReadable()
console.log(`GPU Memory: ${readable}`) // e.g., "45.32 MB"
```

## Performance Tips

1. **Mesher Workers**: Increase `mesherWorkers` on multi-core systems
2. **Smooth Loading**: Enable `_experimentalSmoothChunkLoading` to prevent frame drops
3. **Clip World**: Use `clipWorldBelowY` to reduce geometry for surface views
4. **Disable Lighting**: Set `enableLighting: false` for faster meshing

## Development

```bash
# Install dependencies
pnpm install

# Run playground
pnpm dev

# Build library
pnpm build

# Type check
pnpm typecheck
```

## File Structure

```
src/
├── index.ts              # Main exports
├── types.ts              # TypeScript types
├── config.ts             # Default configurations
├── appViewer.ts          # Main application viewer
├── worldView.ts          # Chunk loading/events (WorldDataEmitter)
├── playerState.ts        # Player state management
├── three/                # Three.js backend
│   ├── index.ts          # Backend exports
│   ├── graphicsBackend.ts    # Main backend entry
│   ├── documentRenderer.ts   # Render loop management
│   ├── worldGeometryHandler.ts   # Chunk geometry
│   └── starField.ts      # Night sky effect
└── playground/           # Development environment
    ├── playground.ts     # Main playground entry
    └── playground.html   # HTML template
```

## Integration Example

```typescript
import { AppViewer, createGraphicsBackend, WorldView } from 'minecraft-renderer'
import ChunkLoader from 'prismarine-chunk'
import WorldLoader from 'prismarine-world'

// Setup world (using prismarine-world)
const World = WorldLoader('1.20.4')
const Chunk = ChunkLoader('1.20.4')
const world = new World().sync

// Create viewer
const viewer = new AppViewer()

// Provide resources (textures, models)
viewer.resourcesManager = {
  currentConfig: { version: '1.20.4' },
  currentResources: {
    blocksAtlasImage: atlasImage,
    blocksAtlasJson: atlasJson,
    blockstatesModels: modelsData,
    allReady: true
  },
  on: () => {}
}

// Load backend
await viewer.loadBackend(createGraphicsBackend)

// Start world
await viewer.startWorld(world, 4) // 4 chunk render distance

// Initialize world view
await viewer.worldView!.init(new Vec3(0, 64, 0))

// Game loop
function gameLoop() {
  viewer.updateCamera(playerPosition, playerYaw, playerPitch)
  requestAnimationFrame(gameLoop)
}
gameLoop()
```

## License

MIT

---
_Source: https://npm.io/package/minecraft-renderer · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
