npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

minecraft-renderer

v0.1.29

Published

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

Readme

Minecraft Renderer

Minecraft Renderer

A modular Minecraft world renderer with Three.js WebGL backend. Designed for performance testing, experimentation, and integration into Minecraft clients.

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.

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.

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)

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:

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:

// 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, skyLight * skyLightMultiplier)

// 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:

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

Configuration

WorldRendererConfig

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
// 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

# 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

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