three-plugin-kit
v1.1.0
Published
Framework-agnostic Three.js plugin system with camera, label, heatmap, and overlay plugins
Maintainers
Readme
three-plugin-kit
A framework-agnostic, modular plugin system for Three.js. Provides production-ready plugins for camera animation, HTML labels, heatmap overlays, alert effects, and particle systems — all wired through a type-safe event bus.
Features
- Plugin Manager — Dependency-resolved lifecycle (register → init → update → dispose)
- Type-safe EventBus — Inter-plugin communication with standard event definitions
- Camera Plugin — Smooth fly-to animations, view directions, ray-based zoom, auto-rotation
- Label Plugin — CSS2DRenderer-based 3D labels with distance culling and custom mount functions
- Heatmap Plugin — Canvas-texture heatmap overlays with configurable gradients and fade transitions
- Alert Overlay Plugin — GLSL shader bounding-box overlays (info / warning / critical)
- XRay Visuals — Framework-agnostic x-ray shader material, edge helpers, and visual presets
- Particle Plugin — Firefly-style particle effects with configurable physics
- Tree-shakeable — Each plugin is a separate entry point; import only what you need
- Zero framework coupling — Works with React, Svelte, Vue, or vanilla JS
Install
npm install three-plugin-kit three
# or
pnpm add three-plugin-kit threethree (>= 0.150.0) is a peer dependency.
Quick Start
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { PluginManager } from 'three-plugin-kit/core'
import { CameraPlugin } from 'three-plugin-kit/camera'
import { LabelPlugin } from 'three-plugin-kit/label'
// Standard Three.js setup
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 0.1, 2000)
const renderer = new THREE.WebGLRenderer({ canvas })
const controls = new OrbitControls(camera, renderer.domElement)
// Create plugin manager
const pm = new PluginManager({ scene, camera, renderer, controls, canvas })
// Register plugins
pm.register('camera', new CameraPlugin())
pm.register('label', new LabelPlugin())
// Initialize (respects dependency order)
await pm.init()
// Render loop
function animate() {
requestAnimationFrame(animate)
const dt = clock.getDelta()
pm.update(dt)
renderer.render(scene, camera)
}
animate()
// Cleanup
pm.dispose()Modules
Core — three-plugin-kit/core
The plugin lifecycle and communication backbone.
import {
PluginManager,
EventBus,
EventNames,
BasePlugin,
type IPlugin,
type PluginContext,
type StandardEvents
} from 'three-plugin-kit/core'Writing a custom plugin:
import { BasePlugin } from 'three-plugin-kit/core'
class MyPlugin extends BasePlugin {
name = 'my-plugin'
dependencies = ['camera'] // optional
async onInit() {
this.on('camera:moveEnd', (data) => {
console.log('Camera stopped at', data.position)
})
}
update(deltaTime: number) {
// Called every frame
}
onDispose() {
// Cleanup
}
}Standard events:
| Category | Events |
| -------- | -------------------------------------------------------------------------------------------------- |
| Camera | camera:moveStart, camera:moveEnd, camera:animating, camera:viewChanged, camera:resetView |
| Object | object:selected, object:deselected, object:hovered, object:hoverStart, object:hoverEnd |
| Model | model:loaded, model:cleared, model:error |
| Input | input:keyDown, input:keyUp, input:click, input:dblclick |
| Label | label:clicked, label:hoverStart, label:hoverEnd |
| FPS | fps:modeChanged, fps:rotationUpdate |
Camera — three-plugin-kit/camera
Smooth camera animations with easing, view presets, and keyboard controls.
import { CameraPlugin } from 'three-plugin-kit/camera'
pm.register('camera', new CameraPlugin())
await pm.init()
const cam = pm.getPlugin<CameraPlugin>('camera')
// Fly to a named object in the scene
await cam.flyTo('server-rack-01', { duration: 1500, padding: 0.1 })
// Fly to a bounding box
await cam.flyToBoundingBox(myBox3, { closeUp: true })
// Reset to preset view
cam.resetCameraView('top') // 'isometric' | 'top' | 'front' | 'side'
// Generic animation
await cam.animateCameraTo(
new THREE.Vector3(10, 5, 10), // position
new THREE.Vector3(0, 0, 0), // lookAt
{ duration: 800 }
)Also includes FPSControlsPlugin, KeyboardMovementPlugin, and KeyboardPanPlugin for first-person and keyboard-driven navigation.
Label — three-plugin-kit/label
DOM elements positioned in 3D space via CSS2DRenderer. Supports plain text, SVG icons, or full framework component mounting.
import { LabelPlugin } from 'three-plugin-kit/label'
pm.register('label', new LabelPlugin())
await pm.init()
const labels = pm.getPlugin<LabelPlugin>('label')
// Simple text label
labels.addLabel({
id: 'sensor-01',
position: { x: 0, y: 2, z: 0 },
content: '23.5 °C',
color: '0, 255, 200',
maxDistance: 50
})
// Mount a custom component (framework-agnostic)
labels.addLabel({
id: 'custom-01',
position: { x: 5, y: 1, z: 0 },
frameless: true,
mountFn: (target) => {
// Mount your React/Svelte/Vue component into `target`
target.innerHTML = '<div class="my-widget">Hello</div>'
return () => {
target.innerHTML = ''
} // cleanup
}
})
// Batch update
labels.setLabels([
{ id: 'a', position: { x: 0, y: 0, z: 0 }, content: 'A' },
{ id: 'b', position: { x: 5, y: 0, z: 0 }, content: 'B' }
])
// Update in-place
labels.updateLabel('sensor-01', { content: '24.1 °C', breathing: true })Key LabelData fields:
| Field | Type | Description |
| -------------- | -------------------- | ---------------------------------------- |
| id | string | Unique identifier (required) |
| position | { x, y, z } | 3D world position (required) |
| offset | { x, y, z } | World-space offset from position |
| content | string | Text content |
| icon | string | SVG string (left side icon) |
| color | string | "R, G, B" format |
| breathing | boolean | Pulse animation effect |
| frameless | boolean | No background / border |
| anchor | { x, y } | CSS2D anchor point (0-1 normalized) |
| anchorBottom | boolean | Shorthand for anchor: { x: 0.5, y: 1 } |
| maxDistance | number | Auto-hide beyond camera distance |
| minDistance | number | Auto-hide below camera distance |
| mountFn | (el) => () => void | Mount custom DOM / component |
Performance tip: Never use backdrop-filter on label elements — it forces a GPU compositing layer per element. Use a high-opacity background instead.
Heatmap — three-plugin-kit/heatmap
Canvas-texture heatmap overlays projected onto mesh surfaces.
import { HeatmapPlugin, type HeatmapDataPoint } from 'three-plugin-kit/heatmap'
pm.register('heatmap', new HeatmapPlugin())
await pm.init()
const heatmap = pm.getPlugin<HeatmapPlugin>('heatmap')
// Register a floor mesh
heatmap.registerMesh({
meshId: 'floor-1',
mesh: floorMesh,
maxVal: 40,
radiusInMeters: 2.5,
gradient: {
'0.0': 'blue',
'0.5': 'cyan',
'0.75': 'yellow',
'1.0': 'red'
}
})
// Set data points (world coordinates)
const points: HeatmapDataPoint[] = [
{ x: 3.2, z: -1.5, val: 28.5 },
{ x: 7.0, z: 2.0, val: 35.0 }
]Includes utility functions: worldToCanvasPixel, physicalRadiusToPixelRadius, formatDataPointsForHeat, computeAndApplyPlanarUV, buildHeatGradient.
Alert Overlay — three-plugin-kit/alert-overlay
Animated GLSL shader overlays for status indicators on any 3D object.
import { AlertOverlayPlugin } from 'three-plugin-kit/alert-overlay'
pm.register('alert', new AlertOverlayPlugin())
await pm.init()
const alert = pm.getPlugin<AlertOverlayPlugin>('alert')
// Set alerts on objects
alert.setAlerts([
{ id: 'rack-01', object: rackMesh, severity: 'info' },
{ id: 'rack-02', object: rackMesh2, severity: 'critical' }
])| Severity | Color | Visual |
| ---------- | ----- | ------------------------------ |
| info | Green | Gentle glow + pulse |
| warning | Amber | Medium pulse + scan lines |
| critical | Red | Intense pulse + glitch effects |
Shader features: Fresnel silhouette glow, fractal aura ray march, signal noise, scan lines, block glitch, drift bands.
XRay Visuals — three-plugin-kit/xray
Reusable x-ray material and edge helpers without application-specific selection semantics.
import {
SciFiXRayMaterial,
createBaseXRayVisualOptions,
createHoverXRayVisualOptions,
createEdgeLines
} from 'three-plugin-kit/xray'
const xrayMaterial = new SciFiXRayMaterial(createBaseXRayVisualOptions())
const hoverMaterial = new SciFiXRayMaterial(createHoverXRayVisualOptions({ alpha: 0.56 }))
const edges = createEdgeLines(mesh)Use this module when you only need the visual building blocks. Object selection, material swapping, and scene-specific reset logic should remain in the application layer.
Particles — three-plugin-kit/particles
Firefly-style glowing particles that rise from 3D objects.
import { ParticlePlugin } from 'three-plugin-kit/particles'
pm.register('particles', new ParticlePlugin())
await pm.init()
const particles = pm.getPlugin<ParticlePlugin>('particles')
// Attach to scene object by name
particles.attachToNode('server-rack-01', {
count: 80,
color: 0xaa66ff,
coreColor: 0xffffff,
riseSpeed: 1.5,
size: 0.25,
lifetime: 2.5
})
// Or attach to any Object3D
particles.attachToObject(myMesh, 'effect-key', { count: 200, color: 0x00ffcc })
// Remove
particles.detachFromNode('server-rack-01')Options:
| Option | Default | Description |
| --------------- | ---------- | ------------------------------- |
| count | 120 | Particle count |
| lifetime | 2.5 | Lifespan in seconds |
| riseSpeed | 1.5 | Upward velocity |
| driftStrength | 0.4 | Horizontal drift |
| color | 0xaa66ff | Base glow color |
| coreColor | 0xffffff | Bright core color |
| size | 0.25 | World-space size |
| spreadFactor | 0.2 | Spawn spread (relative to bbox) |
Utils — three-plugin-kit/utils
Camera math utilities.
import { calculateOptimalDistance, selectViewDirection } from 'three-plugin-kit/utils'
// Compute camera distance to fit object in viewport
const distance = calculateOptimalDistance(boundingBox, camera, viewDirection, 0.15)
// Auto-correct edge-on views to isometric
const safeDir = selectViewDirection(currentDirection, boundingBox)Architecture
PluginManager
├── init() → topological sort by dependencies → plugin.init(context)
├── update(dt) → plugin.update(dt) for each plugin
├── dispose() → plugin.dispose() in reverse order
└── EventBus → type-safe pub/sub shared across all plugins
PluginContext (injected into every plugin)
├── scene, camera, renderer, controls, canvas
├── eventBus
└── getPlugin<T>(name)Plugins are framework-agnostic — they never import React, Svelte, or Vue. Bridge to your UI framework in your application code (e.g., subscribe to EventBus events in a React useEffect or Svelte $effect).
License
Proprietary — Copyright (c) 2026 CainChu. All rights reserved. See LICENSE for details.
