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

@needle-tools/three-mesh-effects

v0.2.3

Published

Three.js loaders and TSL effects for Needle Mesh Baker outputs.

Downloads

2,013

Readme

Three.js loaders and TSL materials for GLBs exported by Needle Mesh Baker.

Install

npm install @needle-tools/three-mesh-effects three

The package supports Three.js r185 and r186. Its Needle Engine adapter is an optional import. The examples use ordinary Three.js and assume an existing renderer, scene, and camera.

Features

  • Hull effects: Restore the saved effect automatically, optionally binding its source model.
  • Octahedral impostors: Load exported glTF extensions with a Three.js TSL material.
  • Deformation rigs: Run IK and secondary motion alongside standard animation clips.
  • Pixel and voxel impostors: Create the display mode selected in the workbench.

Quick start

Register once, then load any Mesh Baker glTF without knowing its extensions:

import { useLoader } from '@needle-tools/three-mesh-effects';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

const loader = useLoader(new GLTFLoader(), renderer);
const { scene: model } = await loader.loadAsync('/model.glb');
scene.add(model);

This handles hull effects, deformation rigs, octahedral impostors, and pixel impostor atlas bindings, including assets combining extensions. It also works with load, parse, and parseAsync. Each handler is dynamically imported only for assets declaring that extension; bundlers can keep those imports in separate chunks. Dynamic imports defer downloads, rather than removing handlers that an asset might need. The existing individual registration functions remain available.

All extensions are enabled by default. To opt out of specific handlers, pass their glTF extension names in the optional third argument:

useLoader(loader, renderer, { disable: ['NEEDLE_mesh_impostor'] });

Supported keys are NEEDLE_hull_effect, NEEDLE_mesh_impostor, NEEDLE_pixel_impostor, and NEEDLE_deformation_rig. Disabled optional extensions keep their portable fallback without importing the handler. A disabled extension listed in the asset's extensionsRequired rejects the load with an error. Calling useLoader again replaces the renderer and disable list for subsequent parses; omitting options enables everything again.

If a model uses Draco, Meshopt, or KTX2 compression, you must configure the corresponding decoder or texture loader on your GLTFLoader yourself. useLoader handles Needle extensions; it does not configure compression decoders. Rig animation still needs a per-frame update, pixel display modes remain available through createPixelImpostorDisplay. Hull effects load alone; optionally use bindHull to add an original model and animation syncing.

When your loader also supports NEEDLE_progressive, impostor shading and fur textures can change resolution in place. Material bindings and size-dependent sampling update when a texture is replaced; fur height keeps its UV channel and texture transforms. Resize impostor atlases per frame rather than filtering across neighboring views. Compact shading alpha stores material data and needs independent channel filtering and lossless PNG encoding. Packed depth/geometry and sparse pixel-impostor records must retain their authored resolution.

Hull effects

When exporting with a hull effect selected, Mesh Baker saves its mode and settings in NEEDLE_hull_effect. useLoader restores it without a second model:

import { useLoader } from '@needle-tools/three-mesh-effects';
import { bindHull, getHullEffect } from '@needle-tools/three-mesh-effects/hull-effects';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

const loader = useLoader(new GLTFLoader(), renderer);
const { scene: hull } = await loader.loadAsync('/hull.glb');
scene.add(hull);
let effect = getHullEffect(hull);

// Optional: show the original inside the hull and follow its animation.
const { scene: original } = await loader.loadAsync('/original.glb');
effect = bindHull(hull, original, { animate: true });
// Each frame: advance your AnimationMixers, then effect.syncAnimation().
// Call effect.dispose() before removing the hull.

bindHull replaces the automatically created effect, preserving its settings. It borrows the original and leaves the caller's hierarchy and resources intact. Get the current effect again after rebinding; old bindings no longer control it. bindHull(hull, null) detaches the original and restores the standalone effect. getHullEffect(hull) returns undefined for an ordinary or disposed hull. Standalone animated hulls can follow their own animation; call syncAnimation() after updating their mixers or deformation rig. TSL effects require WebGPURenderer from three/webgpu, using either its WebGPU or WebGL2 backend.

The existing manual API remains available for models without the extension. Here the hull supplies the effect's surface and the original stays visible inside: Available modes and their defaults are in HULL_EFFECT_MODES and createHullEffectValues():

Hull effect preview

import { createHullEffect, createHullEffectValues } from '@needle-tools/three-mesh-effects/hull-effects';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

const loader = new GLTFLoader();
const [{ scene: hull }, { scene: original }] = await Promise.all([
  loader.loadAsync('/hull.glb'), // Exported by Mesh Baker.
  loader.loadAsync('/original.glb'), // The model uploaded to Mesh Baker.
]);
scene.add(hull);
const effect = createHullEffect(hull, original, createHullEffectValues().flames);
// For animated models, advance both mixers and call effect.syncAnimation() each frame.
// Call effect.dispose() at teardown.

Hull extension contract

NEEDLE_hull_effect version 1 is stored on the glTF scene containing the hull:

{ "version": 1, "mode": "flames", "values": { "height": 0.51, "speed": 1 } }

The name appears in extensionsUsed. Exported hull geometry and standard materials remain a portable fallback, so it is not marked required. mode must be a supported HULL_EFFECT_MODES value; omitted controls use their documented defaults, finite numeric values are clamped to their control ranges. Unknown versions, unknown modes, and non-finite or non-numeric control values fail explicitly. The extension stores no source URL and triggers no source download. Each scene with the extension gets its own runtime; dispose each when unloading.

Octahedral impostors

An 800,000-triangle source represented by a two-triangle impostor:

Octahedral impostor example

Register the extension loader before loading an exported impostor GLB:

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { registerNeedleImpostorLoader } from '@needle-tools/three-mesh-effects/impostor-runtime';

const loader = new GLTFLoader();
// Register before loading so the extension can replace the portable fallback.
registerNeedleImpostorLoader(loader, { renderer });
const { scene: model } = await loader.loadAsync('/result_impostor.glb');
scene.add(model);

Deformation rigs

For a rigged GLB, register the extension before loading and advance the rig once per frame. Update the animation mixer first:

import { AnimationMixer, Clock } from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { registerNeedleDeformationRigLoader, getDeformationRig } from '@needle-tools/three-mesh-effects/deformation-runtime';

const loader = new GLTFLoader();
// Register before loading so the rig extension is available while parsing.
registerNeedleDeformationRigLoader(loader);
const { scene: riggedModel, animations } = await loader.loadAsync('/rigged.glb');
scene.add(riggedModel);
const clock = new Clock();
const mixer = new AnimationMixer(riggedModel);
if (animations[0]) mixer.clipAction(animations[0]).play();
const rig = getDeformationRig(riggedModel);
renderer.setAnimationLoop(() => {
  const dt = clock.getDelta();
  mixer.update(dt);
  rig?.update(dt);
  renderer.render(scene, camera);
});

Standard animation clips use an ordinary Three.js AnimationMixer. Update the mixer before rig.update(deltaSeconds) when also using IK or secondary motion. The runtime respects the animated bone frame; it does not play the clip itself. Without procedural rig features, the mixer and standard GLTFLoader are sufficient.

Pixel and voxel impostors

Pixel and voxel impostors have a portable glTF shell. Register the loader and create the display that matches the chosen workbench mode:

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { registerNeedlePixelImpostorLoader, createPixelImpostorDisplay } from '@needle-tools/three-mesh-effects/pixel-impostor-runtime';

const loader = new GLTFLoader();
// Register before loading so the pixel atlas is available while parsing.
registerNeedlePixelImpostorLoader(loader);
const { scene: pixelModel } = await loader.loadAsync('/pixel.glb');
scene.add(pixelModel);
const display = createPixelImpostorDisplay(pixelModel, { renderer, mode: 'screen-pixels', pixelSize: 5 });
// Call display.updateViewport() after a resize and display.dispose() at teardown.

Needle Engine and glTF fallbacks

impostor-runtime/needle-engine is a separate optional import that requires @needle-tools/engine; ordinary Three.js applications never import it.

Each GLB has a portable fallback. Register its extension loader to reproduce the workbench's full result. See the download-time snippet in the workbench for the code and settings specific to an exported model.

Contact

Questions or feedback? Email [email protected].