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

@takahirox/three-mmd

v0.1.3

Published

MMD loading, animation, physics, TSL materials, outlines, and VPD export for Three.js

Readme

@takahirox/three-mmd

Standalone MMD addons restored from Three.js r171, after the MMD modules were removed from the official addons in r172. Loads PMD/PMX models, VMD animation, and VPD poses, with animation, IK, physics, toon shading, and VPD export.

The package targets Three.js r186 (three ~0.186.0) and is tested with three 0.186.1. Three.js is a peer dependency: the addons use the application's Three.js instance, including its retained TGALoader and SkeletonUtils addons. mmd-parser ^1.1.4 is an external runtime dependency installed automatically with this package; its implementation and public types come from that package.

Install the package alongside the supported Three.js version:

npm install @takahirox/three-mmd three@~0.186.0

Release candidate 0.1.3 updates the registry-confirmed published 0.1.2 with canonical PMD/PMX texture-resource paths before loading and caching, including Windows separators, safe dot segments and encoded filenames. Unsafe relative paths are rejected before handler lookup or fetching. It retains the optional pinned Bullet WASM and classic Worker physics. Rare severe PMD physics instability remains under investigation; native MMD rendering parity and Android image-decoding fixes are unverified. See the release record for source, registry and validation evidence. This candidate has not been published by this task.

Use native ES module imports, either from the package root or from addon-style subpaths:

import { MMDLoader, MMDAnimationHelper, MMDOutlineEffect } from '@takahirox/three-mmd';
import { WebGPURenderer } from 'three/webgpu';

// Equivalent individual imports:
// import { MMDLoader } from '@takahirox/three-mmd/loaders/MMDLoader.js';
// import { MMDAnimationHelper } from '@takahirox/three-mmd/animation/MMDAnimationHelper.js';

const loader = new MMDLoader();
const mesh = await loader.loadAsync( 'model.pmx' );
const helper = new MMDAnimationHelper();
helper.add( mesh, { physics: false } );

const renderer = new WebGPURenderer( { antialias: true } );
await renderer.init(); // WebGPU, with Three.js WebGL2 fallback when unavailable
const outlines = new MMDOutlineEffect( renderer );

// Add mesh to your scene, call helper.update(deltaSeconds), then:
// outlines.render(scene, camera);

The public subpaths are:

| Subpath (after @takahirox/three-mmd/) | Named exports | | --- | --- | | loaders/MMDLoader.js | MMDLoader | | animation/MMDAnimationHelper.js | MMDAnimationHelper | | animation/CCDIKSolver.js | CCDIKSolver, CCDIKHelper | | animation/MMDPhysics.js | MMDPhysics | | exporters/MMDExporter.js | MMDExporter | | materials/MMDToonMaterial.js | MMDToonMaterial | | effects/MMDOutlineEffect.js | MMDOutlineEffect | | materials/MMDUV.js | mmdAdditionalUV |

All named exports are also available from @takahirox/three-mmd. The APIs follow the r171 addons except for the migrated rendering path described below. For parser APIs and raw model/motion/pose types, import directly from mmd-parser:

import { Parser, CharsetEncoder } from 'mmd-parser';

The internal libs/mmdparser.module.js subpath and package-root MMDParser, Parser, and CharsetEncoder exports have been removed. MMDExporter exports VPD poses, not PMD/PMX models or VMD animations.

Synchronous physics requires an initialized Ammo.js runtime exposed as globalThis.Ammo before creating MMDPhysics or adding a mesh with physics enabled (the helper's default). Ammo is supplied by the application. The Examples and primary regression tests use the Ammo-compatible WASM build of official Bullet commit d3afe76132fc76b0f9b2359cbaef47a3766e7fbb by default. The npm package includes the same optional runtime, manifest and licenses in dist/vendor/bullet/. Copy that entire directory from the installed package (node_modules/@takahirox/three-mmd/dist/vendor/bullet/) to your served vendor/bullet/ directory, then load its classic script:

<script src="./vendor/bullet/ammo.js"></script>
// Await initialization before creating physics.
const ammoURL = new URL( './vendor/bullet/ammo.js', document.baseURI );
globalThis.Ammo = await globalThis.Ammo( {
  locateFile: path => new URL( path, ammoURL ).href
} );
helper.add( anotherMesh );

The library continues to accept your own compatible Ammo.js build, including ammojs-typed and asm.js; it does not select or download a backend. WebAssembly requires browser support and a separately served binary; use application/wasm, preferably on the same origin. Handle setup errors in your application. The Examples enforce a 10-second initialization deadline, verify the instantiated WASM exports and pinned source identity, and stop on failure. Worker recovery uses the initialized main-thread engine. There is no automatic fallback to the old Bullet build.

For optional Worker physics, keep the main-thread Ammo initialization above for recovery, and pass the same served glue URL when adding a mesh:

const workerMesh = await loader.loadAsync( 'model.pmx' );
helper.add( workerMesh, {
  physicsMode: 'worker',
  workerPhysics: { ammoURL: ammoURL.href }
} );
await helper.objects.get( workerMesh ).physics.ready;

The classic Worker is included at dist/animation/MMDPhysicsWorker.js and is resolved relative to the installed runtime module. Preserve that file when bundling or copying runtime modules. For custom hosting/CSP, provide workerPhysics.createWorker: () => new Worker('/your/MMDPhysicsWorker.js'); serve the bundled classic Worker on the same origin. No extra npm dependency is needed. Worker glue resolves ammo.wasm beside its ammoURL. Without cross-origin isolation it uses transferables; isolated pages can use shared memory. See Worker physics for behavior and recovery.

Normal Examples need no Bullet query parameter. ?bullet=legacy explicitly rolls back to [email protected] WASM for diagnostics; ?bullet=master and ?bullet=default are aliases for the new default. There is no engine selector. See default runtime verification and rollback and build provenance and historical comparison. This switch does not establish a fix for intermittent 120 FPS instability. Pass { physics: false } when adding a mesh to use animation without Ammo. Model texture decoding and rendering require a browser environment. The mmd-parser dependency supports PMX UTF-16LE text and SDEF skinning type 3 with right-handed center vectors. Version 1.1.3 is the minimum parser version for SDEF. PMX SDEF uses quaternion skinning on both TSL backends, including vertex morphs, normals, outlines and shadows. See SDEF and private model viewing for the algorithm, tests and optional local YYB Miku workflow.

Direct PMX type 2 bone morphs use mmd-parser 1.1.4 or newer. The animation helper applies their existing morphTargetInfluences weights after VMD tracks and before IK, grants and physics, including static and paused updates. helper.enable('boneMorph', false) disables bone offsets. See bone morph playback and private model inspection for composition, pose/seek behavior, supported scope and local viewer controls.

MMD rendering uses TSL / NodeMaterial. MMDToonMaterial extends Three.js MeshPhongNodeMaterial, with dedicated MMD ambient and color composition, toon direct irradiance, separate Phong specular and MMD sphere mapping (matcap plus matcapCombine: MultiplyOperation for .sph, AddOperation for .spa). PMX SubTexture mode 3 uses matcapMode: 'subtexture' to multiply RGBA at morphed additional UV1 XY before specular lighting. Standard color/opacity, diffuse and alpha textures, user emissive, normal/bump/displacement maps, sidedness and transparency use Three's node material facilities. diffuse remains an alias for color. Set material.needsUpdate = true after changing texture presence or sphere blend mode, as with other Three.js material shader configuration changes.

Use WebGPURenderer from three/webgpu; { forceWebGL: true } selects the WebGL2 TSL backend explicitly. Both backends use the same MMD material and outline implementation. The legacy GLSL MMDToonShader export/subpath and WebGL-only OutlineEffect integration have been removed.

MMDOutlineEffect wraps renderer.render(scene, camera) with an inverted, expanded BackSide hull, adapted from Three.js r186 ToonOutlinePassNode. effect.enabled toggles outlines globally; effect.dispose() releases its cached materials (also released when source materials are disposed). Per-material userData.outlineParameters retains visible, thickness, color (RGB array), and alpha. PMD edge flags and PMX edge size/color/alpha remain independent across material groups. The mmdEdgeRatio geometry attribute includes PMX per-vertex edgeRatio and PMD per-vertex edge disable flags, multiplying extrusion thickness. Outlines use the same mesh and Three's morphing/skinning/displacement setup as the surface, so animation requires no separate outline mesh or skeleton synchronization.

PMX material morphs update surfaces, sampler colors, alpha and outlines through TSL uniforms. The helper evaluates direct and group weights from immutable PMX bases; helper.enable('materialMorph', false) restores those bases. Static sliders use helper.update(0).

PMX group morphs support direct vertex, bone, UV and material targets, including VMD playback and a private local inspection viewer.

PMX UV and additional UV morphs support direct/group weights, VMD playback and TSL vec4 channel access.

PMD/PMX texture resource paths normalize relative filenames before cache lookup, handler selection and loading.

Development and validation:

npm ci
npm run typecheck
npm test
npm run test:browser
npm pack

Try the public browser examples. They are built from this repository and automatically deployed after updates to main. See the examples guide for the workflow and one-time GitHub Pages setup.

Run the browser examples locally:

npm run examples:assets
npm run dev

Open http://127.0.0.1:8080/ for model animation with IK/physics, synchronized audio/camera animation, and VPD poses. MMD modules come from this checkout. The asset setup downloads the former r171 example assets and their notices for the examples; these assets have separate terms and are excluded from Git and the npm package. The deployed site includes the assets and their notices. The four PMD demos retain ambient color 0xAAAAAA and white directional light, both at intensity 1 (Issue #100). Gene Light PMX uses 0x888888 for both lights at intensity 1 (Issue #102); see the measured overbrightness investigation. These Examples/research changes are excluded from the npm runtime. Maintainer visual review remains optional; native MMD fidelity and resolved PMX white clipping are not established. See the examples guide for credits, manual setup, and checks.

The published artifact contains compiled native ES modules and TypeScript declarations; consumers need no build step. Maintained sources are TypeScript and npm run build emits JavaScript and declarations into dist/. npm test and the browser/example commands build the package first; npm pack builds it through prepack. Manual npm publish also runs strict type checks and the offline Node/package regression tests before uploading. See the release guide for maintainer prerequisites and dry-runs. Validate the 0.1.3 candidate before publication by installing the local takahirox-three-mmd-0.1.3.tgz produced by npm pack. TypeScript consumers should install matching Three.js types with npm install --save-dev @types/three@~0.186.0. Root and documented .js subpath imports resolve to the same API in both JavaScript and TypeScript.

The loader and animation helper use public result types from mmd-parser; Three.js geometry, IK, and other transformed data keep their local types. See the migration notes. Tests check all public imports, parse generated PMD/PMX/VMD assets, exercise animation, IK, VPD round trips, node material properties and edge ratios, and real Ammo physics. They also pack the package, install the tarball in an isolated consumer, and import every public module without access to the repository's source files. The isolated consumer also type-checks every public subpath, including checks that invalid API calls are rejected. Examples, scripts, and tests also use strict TypeScript. npm run check:typescript rejects JavaScript sources outside generated dist/ output. Node.js 20 or newer runs the repository scripts and tests through tsx.

The separate browser regression suite requires Google Chrome. Set CHROME_BIN to its executable if it is not installed at the default location (on Linux, the default command is google-chrome). It uses headless Chrome with software WebGL2 through WebGPURenderer({ forceWebGL: true }) to test actual rendered pixels for toon gradients, diffuse textures, Phong specular, emissive, alpha, sidedness and additive/multiplicative sphere mapping. It verifies per-material outline visibility/thickness/color/alpha, per-vertex edge ratios and combined skinning/morphing. Generated textures and PMD/PMX models keep these checks independent of external assets and image decoding. When Chrome supplies a WebGPU adapter, the same assertions run on native WebGPU; otherwise the suite reports that capability-gated rendering was skipped. It also validates the actual example pages using generated MMD/audio fixtures, including rendering, animation, controls, audio playback, and VPD poses.

Source provenance and licenses are recorded in THIRD_PARTY_NOTICES.md and LICENSE.

See the development flow, testing and CI principles, and review guidelines for contribution guidance.

PMX rotation/translation grants, including local grants and physics phase ordering and layered authored/body/post-physics composition, are documented in docs/grants.md, with generated fixture tests and a private loopback inspector.

The loopback-only npm run dev:sdef viewer also provides Generated Physics layers fixture and a private physics-layers/ source. It runs real Ammo with step/pause/reset controls and reports authored, pre-physics, body and final transforms. Post kinematic inputs are explicitly deferred to the next step; dynamic post effects deform the skeleton without teleporting mode-1 bodies. Private models remain optional, ignored and excluded from published artifacts.

Physics forwards positive elapsed seconds to Bullet, which accumulates short frames using the default 1/60 fixed step. Zero, negative and non-finite deltas do not advance Bullet. See the timekeeping experiment and validation for the removed legacy minimum and its possible effect on motion/settling.

PMD and PMX share the same physics policy after coordinate normalization. Type 2 keeps skeletal bone translation and Bullet rotation without teleporting the native dynamic body. Authored-pose restoration, zero-time evaluation, reset and warmup also share one policy; PMX before/after-physics features remain supported. See the format audit, validation and rollback.

PMD and PMX rigid bodies enable Bullet's additional damping during construction in sync, shared sync and Worker physics. Model-defined linear/angular damping is retained, and Bullet's additional factors and thresholds stay at their defaults. This experiment targets low-speed jitter; it does not establish a fix for violent instability or exact native MikuMikuDance behavior. See the additional damping validation.

Joint-connected bodies can collide when both model collision masks permit it. This differs from legacy Three.js and can increase vibration when overlapping bodies' contact separation competes with joint limits. See the linked-body collision experiment and validation.

Joint frames and rigid-body offsets compose parsed right-handed Euler angles in YXZ order in Sync and Worker physics. The joint convention is a reversible compatibility experiment; exact native MMD parity and improved long-running stability remain unproven. See the joint-frame rationale and validation.

Opt-in real-model setup: npm run models:setup -- gene, then npm run dev:sdef and choose Gene in Installed models. Safe manual acquisition import: npm run models:import -- /path/to/model.zip --category sdef. See local model setup, source terms and diagnostics.

Optional worker physics

Omitting physicsMode, or selecting 'sync', preserves current-frame physics and standalone MMDPhysics behavior. Select 'worker' at setup to run Ammo in a classic Worker and display the latest completed result:

// Keep main-thread Ammo initialized (as above) to allow automatic sync fallback.
helper.add( mesh, {
  animation,
  physics: true,
  physicsMode: 'worker',
  workerPhysics: {
    ammoURL: new URL( './vendor/bullet/ammo.js', document.baseURI ).href,
    onFallback: reason => console.warn( reason )
  }
} );
const physics = helper.objects.get( mesh )!.physics!;
await physics.ready; // optional setup-time readiness; never await per frame
helper.update( delta );

ammoURL must serve a compatible classic Ammo script exposing an Ammo factory or initialized API. Each Worker initializes its own Ammo. For the pinned WASM build, copy the complete dist/vendor/bullet/ directory, keeping ammo.js and ammo.wasm together with their manifest and licenses. The Worker resolves factory locateFile paths relative to ammoURL, independent of the page and Worker bundle directories. Keep a compatible initialized main-thread runtime available for sync fallback. The packaged animation/MMDPhysicsWorker.js bundles its dependencies and needs no import map. Bundlers/deployments must preserve this asset beside MMDPhysics.js; use workerPhysics.createWorker: () => new Worker( yourWorkerURL ) to override its location or satisfy CSP. Allow the Worker URL in worker-src, the classic Ammo glue in script-src, and the binary in connect-src; restrictive policies may also need script-src 'wasm-unsafe-eval' for WASM compilation. Use same-origin URLs and serve over HTTP(S), not file:. COOP/COEP is only needed for SAB, not for WebAssembly or transferable mode. Standalone construction accepts these same options as its fourth argument. Mode selection is fixed at setup; dispose/recreate to change it.

Transport is automatic: SharedArrayBuffer with atomic publication when runtime capability and cross-origin isolation are available, otherwise reusable transferable ArrayBuffers. COOP/COEP headers are optional. There is one request in flight per world. Main-thread frames never wait, spin or call Atomics.wait. While busy, the rendered pose reuses the last complete result; unsent skeletal poses are skipped and elapsed time is coalesced up to 0.25 seconds. Bullet's maxStepNum (default 10, overridable through helper.add(mesh, { maxStepNum })) still limits catch-up at main's unitStep = 1/60 seconds. A 100ms update can integrate about six substeps instead of the previous cap of three, increasing CPU work on slow frames. Longer stalls, including a full 0.25s Worker batch, can still discard simulation time. Substeps use the supplied kinematic pose; they do not reconstruct intermediate animation poses. This is a bounded timekeeping experiment, not a demonstrated cure for physics oscillation or proof of native MMD parity. See the validation record. About one frame of visual latency is typical, but a busy Worker can take longer.

Raw results are projected between the existing before/after-physics phases. PMX post IK/grants and dynamic-parent reprojection remain on the main thread. The Worker aligns mode-2 body positions immediately after its own step, before accepting the next input. Post-phase kinematic inputs use the preceding displayed final pose. Under delay, this pose differs from the current-frame sync pose; animation-driven contacts can therefore differ. For identical sequenced inputs, both backends use the same Bullet/body rules. This is an intentional responsiveness/visual-latency tradeoff, not a strict sync replay.

Startup introduces the initial authored animation pose over the first 30 physics warmup cycles, then settles at full influence for the remaining 30 default cycles. Shorter warmup: N values ramp over min(30, N) cycles; warmup: 0 evaluates the full initial pose without stepping physics. The authored animation time stays fixed throughout startup. Bone positions and shortest-arc rotations blend before morphs, IK, grants and PMX phase processing; post-physics bone results never become the next ramp input. animationWarmup: false opts out of this animation startup transition. To use immediate full-pose settling instead, add with warmup: 0, then call the resulting physics adapter's warmup(60). See initialization policy and validation.

ready resolves after model initialization or fallback; async warmup(cycles) settles one frame per completion at fixed animation time through both pose phases. Normal mesh playback waits for queued startup warmup to complete; ready alone does not indicate that warmup has finished. workerStatus reports initialization, remaining warmup cycles, in-flight work and the last completed sequence. physicsMode and transport report the actual backend and transport. Zero-time updates do not advance Bullet; PMX updates only reproject the last complete body results. Gravity changes are ordered in the Worker. Helper enable/disable suspends submissions/warmup and invalidates pending results. Reset captures the pre-physics pose and invalidates previous results; loop resets use the same path. Removal releases that model's Ammo resources and preserves other shared participants. Disposing the last model terminates the Worker and releases communication resources.

Set helper.sharedPhysics = true before adding models and select 'worker' for every participating model. They share one Worker, world, step and coherent result packet. An existing Ammo world cannot be passed into worker mode. world, bodies and constraints expose native objects only in sync mode; worker mode exposes snapshots in bodyResults and supports createHelper().

Unsupported environments, missing configuration, initialization/runtime errors and watchdog timeouts (default 10 seconds; workerPhysics.timeout overrides it) warn, call onFallback, terminate the Worker and rebuild the whole shared world synchronously with cleared velocities. Keep main-thread Ammo initialized for this fallback. If it is absent, initializationError explains why physics is frozen; ready still settles so an application can report the error. The Worker needs no main-thread Ammo when it operates successfully.

Open the dedicated Worker Physics example from the gallery, or run npm run examples:assets then npm run dev locally. Its entry point requests Worker mode; Model and animation requests sync. Choose the example from the gallery to select the execution mode; both share the same scene and controls. See the comparison and fallback guide. Each page's initial mode is fixed; ?physicsMode=... does not override it. Their status shows the actual backend/transport, any Worker fallback diagnostic, and mean main-thread helper-update time. Compare the same scene and device after warmup; this measures helper CPU time, not universal FPS improvement or end-to-end latency. No isolation headers are needed for this demonstration. Browser tests additionally serve an isolated origin to exercise SAB when supported.