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

three-plugin-kit

v1.1.0

Published

Framework-agnostic Three.js plugin system with camera, label, heatmap, and overlay plugins

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 three

three (>= 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.