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

@trap_stevo/morphal

v0.0.5

Published

The flagship entity framework for building scalable, reusable, and visually stunning entities.

Readme

🌌 @trap_stevo/morphal

The flagship entity framework for building scalable, reusable, and visually stunning entities.
Compose entities from parts, drive them with behaviors, style them with flexible materials, and scale with instancing and performance primitives.


🚀 Features

  • 🧩 Declarative Entities – JSON-like specs for parts, transforms, collisions, and behaviors
  • 🎨 Material Factories – Presets like basic, emissive, pbr-normal, sprite, etc., with unlimited extra options
  • 🎭 Behavior System – Lifecycle-driven behaviors (fadeIn, fadeOut, float, rotate, pulseEmissive, …)
  • 📦 Instancing Pools – Massive scenes via InstancedSpritePool and InstancedMeshPool
  • 🧭 Performance Toolkit – Distance culling, impostor swapping, and entity pooling
  • 🧰 Anchors & Metadata – Built-in anchors (tip, center, …) and collision accessors
  • 🌍 2D & 3D Unified – Mix HUD overlays (orthographic) with world-space entities (perspective)

⚙️ System Requirements

| Requirement | Version | | -------------- | ------------------------- | | Browser | Chrome ≥ 91, Firefox ≥ 90 | | JavaScript | ES2020+ | | npm | ≥ 9.x (recommended) | | OS | Windows, macOS, Linux |


🧩 Spec Reference

Entity Spec (Morphal.create(spec, ctx)) — Core Fields

| Key | Type | Description | Default | | --------------- | -------- | ----------------------------------------- | ------------------ | | name | string | Entity name | "morphal-entity" | | orient | "view" \| "yaw" \| "none" | Camera alignment mode | undefined | | zOffset | number | Layering priority (higher draws on top) | undefined | | materialProps | object | Props applied to all root-level materials | {} | | transform | object | { position?, rotation?, scale? } | {} | | parts | array | List of part specs | [] | | behaviors | array | List of behavior configs | [] |

transform structure:

  • position: { x, y, z }
  • rotation: { x, y, z } (radians)
  • scale: number or { x, y, z }

Part Spec — Common Fields

| Key | Type | Description | Default | | --------------- | -------- | -------------------------------------- | ------------------------------------------------ | | name | string | Unique per entity | auto | | type | string | Registered part type | required | | material | string | Material preset name | preset-specific | | color | string | Base color | preset-specific | | opacity | number | 0..1 | preset-specific | | orient | "view" \| "yaw" \| "none" | Per-part camera alignment | undefined | | zOffset | number | Layering priority for the part | undefined | | transform | object | { position?, rotation?, scale? } | {} | | behaviors | array | [{ type, ...options }] | [] | | materialProps | object | Extra material overrides | {} | | collision | object | { dim:2\|3, tags:[], idPrefix?, isStatic?, metadata? } | undefined | | ...rest | any | Forwarded to the specific part factory | — |

Behavior Config — Common Fields

| Key | Type | Description | Default | | ------------ | -------- | ------------------------- | ------------ | | type | string | Behavior name | required | | ...options | any | Behavior-specific options | — |

Included behaviors (examples):

  • fadeIn({ duration, fromScale })
  • fadeOut({ duration, scaleTo })
  • float({ axis, speed, amplitude })
  • rotate({ axis, speed })
  • pulseEmissive({ frequency, intensity })

📚 Methods Overview

Morphal (Core) — Methods

| Method | Description | Async | | --------------------------------- | -------------------------------------------------------------------- | :---: | | registerPart(name, factory) | Register a part factory | ❌ | | registerBehavior(name, factory) | Register a behavior factory | ❌ | | registerMaterial(name, factory) | Register a material factory | ❌ | | getRegistry() | Access registries (parts, behaviors, materials) | ❌ | | traverseMaterials(object, cb) | Visit all materials in a subtree | ❌ | | setMaterialProps(object, props) | Assign props to all materials in a subtree | ❌ | | makeMaterial(name, params, ctx) | Create a material from a registered factory | ❌ | | create(spec, ctx) | Build an entity; returns { group, update, destroy, bus, findPart } | ❌ |

Entity — Methods

| Method | Description | Async | | --------------------- | -------------------------------------- | :---: | | group | Root node | — | | update(time, delta) | Advance all behaviors | ❌ | | destroy() | Dispose resources & detach from parent | ❌ | | findPart(name) | Retrieve a child part by name | ❌ |

Collision Accessors (per-part)

part.userData.morphal.collision (if enabled in the part spec):

| Method | Description | Async | | ------------------------------- | --------------------------------------------------------------------------- | :---: | | snapshotMatrix() | Copy world matrix into a stable buffer | ❌ | | getMatrixRef() | Get the stable Float32Array(16) matrix reference | ❌ | | getWorldOBB() | Compute world OBB or small sphere | ❌ | | forEachWorldTriangle(consume) | Stream all world-space triangles into consume(ax,ay,az,bx,by,bz,cx,cy,cz) | ❌ |

Performance Toolkit

| Method / Class | Description | Async | | ---------------------------------------------------------------------------------------- | --------------------------------------- | :---: | | Perf.culling.register({ node, maxDist, onCull?, setVisible? }) | Distance-based visibility control | ❌ | | Perf.culling.unregister(entry) | Remove from culling set | ❌ | | Perf.culling.update(camera) | Update all culling entries | ❌ | | Perf.impostorSwitch.register({ node, camera, near, far, poolKey, poolTexture, scene }) | Swap far meshes for billboard instances | ❌ | | Perf.impostorSwitch.unregister(rec) | Remove impostor entry | ❌ | | Perf.impostorSwitch.update() | Evaluate swaps | ❌ | | EntityPool({ create, warm }) | Simple pooling of entities | ❌ | | EntityPool.acquire() | Acquire an entity from the pool | ❌ | | EntityPool.release(entity) | Return entity to the pool | ❌ |

Instancing Pools

| Class / Method | Description | Async | | --------------------------------------------------------------- | -------------------------------- | :---: | | Instancing.pools | Registry of named pools | — | | Instancing.get(key) | Get a pool by key | ❌ | | Instancing.set(key, pool) | Register a pool | ❌ | | Instancing.delete(key) | Dispose + unregister a pool | ❌ | | new InstancedSpritePool({ key, capacity, texture, faceMode }) | Instanced sprites billboard pool | ❌ | | pool.alloc({ x, y, z, sx, sy, opacity }) | Allocate an instance | ❌ | | pool.freeId(id) | Free an instance by id | ❌ | | pool.updateBillboards(camera) | Align billboards to camera | ❌ | | pool.dispose() | Dispose pool resources | ❌ | | new InstancedMeshPool({ key, capacity, geometry, material }) | Instanced mesh pool | ❌ | | pool.alloc(matrix4) | Allocate an instance | ❌ | | pool.update(id, matrix4) | Update instance transform | ❌ | | pool.freeId(id) | Free an instance | ❌ | | pool.dispose() | Dispose pool resources | ❌ |


🧱 Part Catalog (Built-ins)

vectorShape

| Option | Type | Description | Default | | ----------------- | -------- | --------------------------------------------------------- | ---------- | | svg | string | Inline SVG markup | "<svg/>" | | curveSegments | number | Tessellation detail | 48 | | common fields | — | name, material, transform, behaviors, collision | — |


billboardPlane

| Option | Type | Description | Default | | --------------------------- | -------------------- | ---------------------------------------- | ----------- | | src / map | string / Texture | Base texture | — | | normalSrc/normalMap | string / Texture | Normal map | null | | emissiveSrc/emissiveMap | string / Texture | Emissive mask | map | | tint | string | Multiply tint color | "#ffffff" | | emissiveColor | string | Emissive color | "#ffffff" | | emissiveIntensity | number | Emissive strength | 1.0 | | alphaTest | number | Alpha cutout threshold | 0.0 | | parallaxDepth | number | Parallax depth effect | 0.0 | | common fields | — | transform, behaviors, collision | — |


tentacle

| Option | Type | Description | Default | | ----------- | -------- | ----------------------------------- | ------- | | segments | number | Number of curve subdivisions (≥ 2) | 12 | | length | number | Tentacle length | 1.2 | | thickness | number | Tube radius | 0.05 | | curvature | number | Sinusoidal curvature factor | 0.6 | | noise | number | Noise factor for wavy displacement | 0.0 | | common | — | color, material, behaviors, collision | — |


sprite

| Option | Type | Description | Default | | ------------ | -------------------- | ----------------------- | --------- | | src / map| string / Texture | Sprite texture | — | | color | string | Tint color | "#fff" | | opacity | number | Alpha | 1.0 | | size | number | Uniform sprite size | 1.0 | | common | — | transform, behaviors, collision | — |


rippleRing

| Option | Type | Description | Default | | ------------ | -------- | ----------------------- | ------- | | radius | number | Ring radius | 1.0 | | thickness | number | Band thickness | 0.1 | | color | string | Base color | "#ffffff" | | opacity | number | Alpha | 0.5 | | segments | number | Radial subdivisions | 32 | | common | — | transform, behaviors, collision | — |


energyBlob

| Option | Type | Description | Default | | ------------ | -------- | -------------------- | ------- | | radius | number | Blob sphere radius | 0.32 | | color | string | Blob emissive color | "#b38cff" | | opacity | number | Alpha | 0.2 | | common | — | material, transform, behaviors, collision | — |


gltf

| Option | Type | Description | Default | | ------------ | -------- | ----------------------------- | ------- | | src | string | Path to GLTF/GLB file | — | | scale | number \| {x,y,z} | Scale factor(s) | 1.0 | | center | boolean| Auto-center loaded model | false | | common | — | transform, behaviors, collision | — |


🎨 Materials (Built-ins)

basic

| Option | Type | Description | Default | | ------------- | --------- | ---------------------- | ----------- | | color | string | Base color | "#ffffff" | | emissive | string | Forces additive blend | null | | opacity | number | Alpha | 1.0 | | transparent | boolean | Transparency | true | | additive | boolean | Additive blending | false |

emissive

| Option | Type | Description | Default | | ----------- | -------- | -------------------- | ----------- | | mode | string | "unlit" or "pbr" | "unlit" | | color | string | Base color | "#000000" | | emissive | string | Emissive color | "#ffffff" | | intensity | number | Emissive intensity | 1.0 |

pbr-normal

| Option (subset) | Type | Description | | ----------------| ---- | -------------------------------- | | color, map | — | Base color & texture | | emissive* | — | Emissive params | | normalMap* | — | Normal mapping | | roughness*/metalness* | — | PBR surface controls | | aoMap* | — | Ambient occlusion | | displacement* | — | Height mapping | | envMap* | — | Environment reflections | | transmission*/ior/thickness | — | Glass parameters | | clearcoat*/sheen* | — | Coatings & fabric sheen |


📦 Installation

npm install @trap_stevo/morphal

---

## 🏗️ Quick Start (HTML)

```js
import { Morphal } from "@trap_stevo/morphal";

// Renderer bootstrap
const appEl = document.getElementById("app");
const renderer = new WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.outputColorSpace = SRGBColorSpace;
appEl.appendChild(renderer.domElement);

// 3D scene & camera
const scene = new Scene();
scene.background = new Color("#0b0b12");
const camera = new PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 500);
camera.position.set(0, 0.8, 3);
scene.add(camera);
scene.add(new HemisphereLight(0xffffff, 0x334466, 0.35));

// 2D HUD scene (orthographic)
const scene2D = new Scene();
const ortho = new OrthographicCamera();
scene2D.add(ortho);
function resizeOrtho() {
  const w = window.innerWidth, h = window.innerHeight;
  ortho.left = -w / 2;
  ortho.right = w / 2;
  ortho.top = h / 2;
  ortho.bottom = -h / 2;
  ortho.near = -1000;
  ortho.far = 1000;
  ortho.updateProjectionMatrix();
}
resizeOrtho();

// Responsive resize
window.addEventListener("resize", () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
  resizeOrtho();
});

// Minimal entity
const entity = Morphal.create({
  name: "demo",
  orient: "view",
  parts: [
    { type: "billboardPlane", src: "/flare.png", scale: 2 },
    { type: "tentacle", color: "#C655FF", segments: 12 }
  ],
  behaviors: [
    { type: "fadeIn", duration: 1.5 },
    { type: "float", axis: "y", speed: 0.4 }
  ]
}, { parent: scene, camera });

// Basic loop
let last = performance.now() / 1000;
(function tick() {
  const now = performance.now() / 1000;
  const delta = now - last;
  last = now;
  entity.update(now, delta);
  renderer.render(scene, camera);
  requestAnimationFrame(tick);
})();

🧪 Examples

1) Vector Shapes (SVG)

Morphal.create({
  parts: [
    {
      type: "vectorShape",
      svg: "<svg><circle cx='0' cy='0' r='0.6'/></svg>",
      material: "basic",
      color: "#a6b2ff",
      opacity: 0.9,
      transform: { scale: { x: 1.2, y: 1.2, z: 1 } },
      zOffset: -1,
      behaviors: [{ type: "pulseEmissive", frequency: 1.25, intensity: 0.25 }],
      materialProps: { transparent: true, opacity: 0.9 },
      collision: { dim: 3, tags: ["eye", "sclera"] }
    }
  ]
}, { parent: scene, camera });

2) Image Billboards

Morphal.create({
  parts: [
    {
      type: "billboardPlane",
      src: "/textures/flare.png",
      scale: 1.25,
      tint: "#9f67ff",
      emissiveIntensity: 2.0,
      zOffset: 2
    }
  ]
}, { parent: scene, camera });

3) Procedural Mesh Part

Morphal.registerPart("energyBlob", (cfg = {}, ctx) => {
  const geometry = new SphereGeometry(cfg.radius ?? 0.32, 48, 48);
  const material = Morphal.makeMaterial(
    cfg.material || "emissive",
    { color: cfg.color || "#b38cff", opacity: 0.2, transparent: true }
  );
  const mesh = new Mesh(geometry, material);
  mesh.userData.anchors = { center: mesh };
  return mesh;
});

4) 2D HUD Overlay (Orthographic)

const hudEntity = Morphal.create(
  { name: "hud", orient: "view", parts: [ /* vector parts… */ ] },
  { parent: scene2D, camera: ortho }
);

5) Collision Export

function gatherCollidable(root) {
  const out = [];
  root.traverse(o => {
    const c = o.userData?.morphal?.collision;
    if (c?.snapshotMatrix) out.push(o);
  });
  return out;
}
const collidables = gatherCollidable(entity.group);
for (const part of collidables) {
  const c = part.userData.morphal.collision;
  c.snapshotMatrix();
  const matrix = new Float32Array(c.getMatrixRef());
  const obb = c.getWorldOBB();
  const tris = [];
  c.forEachWorldTriangle((ax, ay, az, bx, by, bz, cx, cy, cz) => {
    tris.push(ax, ay, az, bx, by, bz, cx, cy, cz);
  });
}

📜 License

See License in LICENSE.md


✨ Morph, Render, Scale

From SVG paths to billboards to procedural meshes, Morphal gives you one unified way to define, manage, and optimize entities at any scale — in both 2D and 3D contexts.