three-cluster-lights
v1.0.13
Published
Thousands of clustered lighting for Three.js
Maintainers
Readme

three-cluster-lights - Library Components
The core library files for the three-cluster-lights system - a high-performance WebAssembly-powered clustered lighting solution for Three.js.
This library enables thousands of light supporting point lights, rect area lights and spotlights. Point lights support goes up to 32,000 lights in the frustum. Includes experimental shadow support via screen-space ray marching and atlas-based shadow maps.
This is only relevant if you plan on building from the source. Otherwise the npm package is far easier to consume.
📁 Library Structure
lib/
├── index.js # Main entry point - exports all public APIs
├── index.d.ts # TypeScript definitions
├── package.json # NPM package configuration
├── README.md # This file
│
├── core/ # Core lighting system
│ ├── cluster-lighting-system.js # Main ClusterLightingSystem class
│ ├── cluster-shaders.js # GLSL shaders and materials
│ └── shadow-atlas.js # Shadow atlas manager (experimental)
│
├── performance/ # Performance monitoring and optimization
│ ├── performance-metrics.js # Metrics (GPUQuery, FPSMeter, etc.)
│ ├── performance-tracker.js # Unified performance tracker
│ └── adaptive-tile-span.js # Adaptive performance tuning
│
├── utils/ # Utilities
│ └── wasm-loader.js # WASM loading with fallback
│
├── visual/ # Visual debugging
│ └── light-markers.js # Visual light markers
│
└── wasm/ # WebAssembly binaries
├── cluster-lights-simd.wasm # SIMD-optimized version
├── cluster-lights.wasm # Standard version
├── cluster-lights-asm.js # JavaScript fallback
└── cluster-lights.c # Source code📦 Installation & Usage
Basic Import
import {
ClusterLightingSystem,
LightMarkers,
PerformanceTracker,
loadWasm,
LightType,
Animation
} from 'three-cluster-lights';Subpath Imports
// Import only what you need
import { ClusterLightingSystem } from 'three-cluster-lights/core';
import { PerformanceTracker } from 'three-cluster-lights/performance';
import { loadWasm } from 'three-cluster-lights/utils';
import { LightMarkers } from 'three-cluster-lights/visual';🔧 Module Overview
core/ - Core Lighting System
cluster-lighting-system.js
Main clustered lighting system implementation. Manages light data, WASM integration, GPU clustering, and shader patching.
Main Class: ClusterLightingSystem
cluster-shaders.js
GLSL shader code specifically for clustered lighting. Includes material patching functions and shader variants.
Exports:
lights_physical_pars_fragment- Shader preamblelights_fragment_begin- Full-featured fragment shaderlights_fragment_begin_optimized- Optimized fragment shaderShaderVariants- Automatic shader variant selectiongetListMaterial()- Material for list visualizationgetMasterMaterial()- Material for master texture
performance/ - Performance Monitoring
performance-metrics.js
Low-level performance monitoring primitives for GPU timing, FPS tracking, CPU time, and memory usage.
Classes:
GPUQuery- GPU timing using WebGL timer queriesFPSMeter- Frames per second with min/max trackingCPUTimer- CPU frame time measurementMemoryMonitor- JavaScript heap memory tracking
performance-tracker.js
High-level unified performance tracker with automatic HTML/CSS injection.
Class: PerformanceTracker
adaptive-tile-span.js
Adaptive performance tuning that automatically adjusts rendering quality based on target FPS.
Class: AdaptiveTileSpan
visual/ - Visual Debugging
light-markers.js
Visual markers for displaying light positions in the scene with instanced rendering.
Class: LightMarkers
utils/ - Utilities
wasm-loader.js
Helper utility for loading WebAssembly modules with automatic SIMD detection and ASM.js fallback.
Function: loadWasm(options)
🔨 Building WebAssembly Modules
The library includes pre-compiled WASM binaries, but you can rebuild them if needed.
Prerequisites
Install Emscripten:
# Install emsdk
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.shBuild Commands
From the lib/ directory:
# Build standard WASM (no SIMD)
npm run build:wasm
# Build SIMD-optimized WASM (requires SIMD support)
npm run build:wasm-simd
# Build ASM.js fallback (for environments without WebAssembly)
npm run build:asm
# Build all versions
npm run build:allOutput Files
wasm/cluster-lights.wasm- Standard WebAssembly module (~50KB)wasm/cluster-lights-simd.wasm- SIMD-optimized version (~55KB)wasm/cluster-lights-asm.js- JavaScript fallback (~100KB)
What Gets Compiled
The WASM modules are compiled from wasm/cluster-lights.c, which implements:
- Light data structures and memory management
- Morton code sorting for spatial coherence
- Light animation updates (circular, wave, flicker, pulse, rotation)
- View-space transformations
- LOD (Level of Detail) calculations
- Bulk operations for performance
Build Optimizations
The SIMD version includes:
-msimd128- Enable 128-bit SIMD operations-msse, -msse2, -msse3, -msse4.1- Enable SSE instructions--closure 1- Google Closure Compiler optimizations-flto- Link-time optimization
API Documentation
ClusterLightingSystem
High-performance clustered lighting system powered by WebAssembly.
Constructor
const lights = new ClusterLightingSystem(
renderer, // THREE.WebGLRenderer
wasmModule, // WebAssembly module instance
near, // Camera near plane
far, // Camera far plane
sliceX, // Cluster grid X resolution
sliceY, // Cluster grid Y resolution
sliceZ, // Cluster grid Z resolution
performanceMode // Optional: enable performance optimizations (default: true)
);Public Methods
Light Management
// Add a light (returns global light index)
const index = lights.addLight({
type: LightType.POINT, // or 'point', 'spot', 'rect'
position: new THREE.Vector3(0, 5, 0),
color: new THREE.Color(1, 0, 0),
intensity: 10,
radius: 15,
decay: 2,
visible: true,
animation: {
circular: { speed: 1, radius: 5 },
pulse: { speed: 1, amount: 0.5, target: PulseTarget.INTENSITY }
}
});
// Fast light addition (skips some checks for bulk operations)
lights.addFastLight(lightConfig);
// Remove a light
lights.removeLight(globalIndex);Light Property Updates
lights.updateLightPosition(globalIndex, position);
lights.updateLightColor(globalIndex, color);
lights.updateLightIntensity(globalIndex, intensity);
lights.updateLightRadius(globalIndex, radius);
lights.updateLightDecay(globalIndex, decay);
lights.updateLightVisibility(globalIndex, visible);
lights.updateLightAnimation(globalIndex, animationConfig);Animation Shortcuts
// Pulse animation
lights.updatePulseSpeed(globalIndex, speed);
lights.updatePulseAmount(globalIndex, amount);
// Flicker animation
lights.updateFlickerIntensity(globalIndex, intensity);
lights.updateFlickerSpeed(globalIndex, speed);
// Wave animation
lights.updateWaveSpeed(globalIndex, speed);
lights.updateWaveAmplitude(globalIndex, amplitude);
// Circular animation
lights.updateCircularSpeed(globalIndex, speed);
lights.updateCircularRadius(globalIndex, radius);
// Rotation animation
lights.updateRotationSpeed(globalIndex, speed);
// Linear animation
lights.updateLinearDuration(globalIndex, duration);
// Generic property update
lights.updateLightAnimationProperty(globalIndex, animationType, property, value);Material Integration
// Patch a material to use clustered lighting
lights.patchMaterial(material);Configuration
// Enable/disable dynamic cluster resolution
lights.setDynamicClusters(enabled);
// Set LOD bias (affects quality/performance tradeoff)
lights.setLODBias(bias);
const bias = lights.getLODBias();Shadows (Experimental)
Two shadow modes are available: screen-space (ray marching against depth buffer) and atlas (budget-based shadow map atlas). Both can be used with any light count, but screen-space is better suited for high light counts where per-light shadow maps are impractical.
// Set shadow mode: 'off', 'screenspace', or 'atlas'
lights.setShadowMode('screenspace');
// Screen-space shadow intensity (0–1, controls global shadow darkness)
lights.setScreenSpaceShadowIntensity(0.5);
const intensity = lights.getScreenSpaceShadowIntensity();
// Atlas shadow budget (max simultaneous shadow-casting lights, renders per frame)
lights.setShadowBudget(8, 4);
// Enable per-light shadow casting (atlas mode)
lights.setLightShadow('point', lightIndex, true, 0.7);
// Query shadow stats
const stats = lights.getShadowStats();
// stats.activeCandidates, stats.cacheSize, stats.atlasSize,
// stats.maxSlots, stats.usedSlots, stats.shadowsPerFrameNote: Shadow support is experimental and may change in future releases. Screen-space shadows use scale-relative thresholds and work across different scene sizes. Atlas shadows use a temporal cache with importance-based budget allocation.
Main Loop
// Call in your render loop
function animate() {
const time = clock.getElapsedTime();
lights.update(time, camera);
renderer.render(scene, camera);
}LightMarkers
Visual markers for light positions using instanced rendering.
Constructor
const markers = new LightMarkers(lightsSystem, {
visible: true,
showGlow: true,
glowRadius: 0.5,
pointGlowRadius: 0.5,
spotGlowRadius: 0.5,
rectGlowRadius: 0.5,
markerScale: 0.05,
colorOverride: null // THREE.Vector3 or null
});Public Methods
markers.init(scene); // Add markers to scene
markers.update(scene); // Update marker positions
markers.dispose(scene); // Remove and cleanup
markers.reinit(scene); // Dispose and reinit
// Configuration
markers.setVisible(visible);
markers.setShowGlow(show);
markers.setGlowRadius(radius);
markers.setMarkerScale(scale);
markers.setColorOverride(color);PerformanceTracker
All-in-one performance monitoring with automatic UI injection.
Constructor
const tracker = new PerformanceTracker(renderer, {
container: document.body,
showFPS: true,
showCPU: true,
showGPU: true,
showMemory: true,
showWASM: true,
showCluster: true,
showRender: true
});Public Methods
tracker.begin(); // Call at start of render loop
tracker.end(); // Call at end of render loop
tracker.dispose(); // CleanupPerformance Metrics (Low-Level)
GPUQuery
const query = new GPUQuery(renderer, "#element-id");
query.start();
// ... GPU work ...
query.end(time);
query.dispose();FPSMeter
const fps = new FPSMeter("#fps", "#minFps", "#maxFps");
fps.update(time);CPUTimer
const cpu = new CPUTimer("#cpu-value");
cpu.begin();
// ... work ...
cpu.end(time);MemoryMonitor
const mem = new MemoryMonitor("#mem-value", "#mem-unit");
mem.update(time);Constants
LightType
LightType.POINT // 0
LightType.SPOT // 1
LightType.RECT // 2Animation (Bitwise Flags)
Animation.NONE // 0x00
Animation.CIRCULAR // 0x01
Animation.LINEAR // 0x02
Animation.WAVE // 0x04
Animation.FLICKER // 0x08
Animation.PULSE // 0x10
Animation.ROTATE // 0x20LinearMode
LinearMode.ONCE // 0 - Play once
LinearMode.LOOP // 1 - Loop continuously
LinearMode.PINGPONG // 2 - Bounce back and forthPulseTarget (Bitwise Flags)
PulseTarget.INTENSITY // 0x01
PulseTarget.RADIUS // 0x02
PulseTarget.BOTH // 0x03RotateMode
RotateMode.CONTINUOUS // 0 - Continuous rotation
RotateMode.SWING // 1 - Swing back and forthLODLevel
LODLevel.SKIP // 0 - Don't render
LODLevel.SIMPLE // 1 - Minimal quality
LODLevel.MEDIUM // 2 - Medium quality
LODLevel.FULL // 3 - Full qualityComplete Integration Examples
Example 1: Basic Setup with PerformanceTracker
import * as THREE from 'three';
import {
ClusterLightingSystem,
PerformanceTracker,
loadWasm,
LightType,
Animation
} from 'three-cluster-lights';
// Setup renderer
const renderer = new THREE.WebGLRenderer({ antialias: true });
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// Load WASM
const wasm = await loadWasm({ preferSIMD: true });
// Create lighting system
const lights = new ClusterLightingSystem(
renderer,
wasm,
0.1, // near
1000, // far
32, 16, 32, // cluster resolution
true // performance mode
);
// Add lights
lights.addLight({
type: LightType.POINT,
position: new THREE.Vector3(0, 5, 0),
color: new THREE.Color(1, 0.5, 0),
intensity: 10,
radius: 15,
animation: {
circular: { speed: 1, radius: 5 }
}
});
// Patch materials
const material = new THREE.MeshStandardMaterial({ color: 0x808080 });
lights.patchMaterial(material);
// Performance tracking
const tracker = new PerformanceTracker(renderer, {
showFPS: true,
showCPU: true,
showGPU: true,
showMemory: true
});
// Render loop
const clock = new THREE.Clock();
function animate() {
requestAnimationFrame(animate);
tracker.begin();
const time = clock.getElapsedTime();
lights.update(time, camera);
renderer.render(scene, camera);
tracker.end();
}
animate();Example 2: With Light Markers
(Simple but aesthetically pleasing instanced light objects)
import { ClusterLightingSystem, LightMarkers } from 'three-cluster-lights';
// ... setup lights ...
// Add visual markers
const markers = new LightMarkers(lights, {
visible: true,
showGlow: true,
glowRadius: 0.5
});
markers.init(scene);
// Update in render loop
function animate() {
// ...
markers.update(scene);
// ...
}Example 3: Bulk Light Addition
// Add many lights efficiently
for (let i = 0; i < 1000; i++) {
lights.addFastLight({
type: LightType.POINT,
position: new THREE.Vector3(
Math.random() * 100 - 50,
Math.random() * 20,
Math.random() * 100 - 50
),
color: new THREE.Color(Math.random(), Math.random(), Math.random()),
intensity: 5 + Math.random() * 10,
radius: 10 + Math.random() * 20
});
}WASM Module Usage
The library includes WebAssembly modules for high-performance light processing:
Loading WASM
// Option 1: Using loadWasm helper (recommended) no config required or manual wasm additions
import { loadWasm } from 'three-cluster-lights';
const wasm = await loadWasm({
preferSIMD: true // Auto-detect and use SIMD if available
});
// Option 2: You can manually override this and load
const wasm = await WebAssembly.instantiateStreaming(
fetch('/node_modules/three-cluster-lights/wasm/cluster-lights-simd.wasm'),
{ env: { emscripten_notify_memory_growth: () => {} } }
);
// Option 3: If you do this, you can set this up in various ways
import wasmUrl from 'three-cluster-lights/wasm/cluster-lights-simd.wasm?url';
const wasm = await WebAssembly.instantiateStreaming(
fetch(wasmUrl),
{ env: { emscripten_notify_memory_growth: () => {} } }
);SIMD vs Standard
- cluster-lights-simd.wasm - SIMD optimized, ~2x faster (recommended if supported)
- cluster-lights.wasm - Standard version for wider browser compatibility
The loadWasm() helper automatically detects SIMD support and loads the appropriate version.
Building from Source
The WASM source is included for transparency and custom builds:
# Build standard version
npm run build:wasm
# Build SIMD version
npm run build:wasm-simd
# Build both and copy to public/
npm run build:wasm:allRequirements: Emscripten SDK (emcc) must be installed
File Organization
├── index.js # Main entry point
├── index.d.ts # TypeScript definitions
├── package.json # NPM package configuration
├── README.md # This file
│
├── core/ # Core lighting system
│ ├── cluster-lighting-system.js # Main ClusterLightingSystem class
│ ├── cluster-shaders.js # GLSL shaders and materials
│ └── shadow-atlas.js # Shadow atlas manager (experimental)
│
├── performance/ # Performance monitoring
│ ├── performance-metrics.js # Low-level performance primitives
│ ├── performance-tracker.js # High-level performance tracker
│ └── adaptive-tile-span.js # Adaptive performance tuning
│
├── utils/ # Utilities
│ └── wasm-loader.js # WASM loading with fallback
│
├── visual/ # Visual debugging
│ └── light-markers.js # Visual light position markers
│
└── wasm/ # WebAssembly modules
├── cluster-lights.c # WASM source code
├── cluster-lights.wasm # Compiled WASM (standard)
├── cluster-lights-simd.wasm # Compiled WASM (SIMD optimized)
└── cluster-lights-asm.js # JavaScript fallbackPerformance Tips
- Use
addFastLight()for bulk operations - Skips some validation checks - Enable performance mode - Constructor parameter enables optimizations
- Adjust cluster resolution - Larger grids for more lights (8x8x8 for 10K+ lights)
- Use LOD bias - Reduce quality for distant lights (
setLODBias()) - Enable dynamic clusters - Automatically adjusts grid size (
setDynamicClusters(true)) - Prefer SIMD WASM - ~2x faster on supporting browsers
- Update only changed properties - Use specific update methods instead of full updates
Browser Compatibility
- WebAssembly: Required (all modern browsers)
- SIMD: Optional, supported in Chrome 91+, Firefox 89+, Safari 16.4+
- WebGL 2: Required for GPU queries
- EXT_disjoint_timer_query_webgl2: Optional, for GPU timing
For older browser support, the library will gracefully degrade performance monitoring features.
Notes
PerformanceTrackerauto-injects its own CSS — no external stylesheet needed- WASM files are pre-compiled and ready to use
- Shadow support is experimental and may change in future releases
- All file names use consistent kebab-case convention
