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

matrix-engine-wgpu

v1.18.15

Published

AI Code Creator (use engine from npm direct render preview)Cast received for android TV, splat point clouds + color animator and vertex animator, Culling 1.14.0 , HZB Ray 1.12.0 MOBA MOBILE ADAPTATION 1.11.0 Big upgrade 1.9.5 ProceduralMesh Morph - 1.9.1

Downloads

3,420

Readme

matrix-engine-wgpu

Author: Nikola Lukić 📧 [email protected] 📅 Version: 1.18.8 2026 Alias The beast


Logo


Description

This project is a stable but also work-in-progress. WebGPU engine inspired by the original matrix-engine for WebGL. It uses the wgpu-matrix npm package as a replacement for gl-matrix to handle model-view-projection matrices.

Published on npm as: matrix-engine-wgpu (not always updated on last - recommended is to use github clone) From [1.18.7] ver npm package is much more stable and usable. All examples can be done by npm services not just github clone. You can also use (links tested). You can use RES like prefix for textures or objects paths. TheBeast is friendly with codepen, worker physics bridge will test host name is it codepen to fix loading link if not it will use local resource always.

import {MatrixEngineWGPU, downloadMeshes} from "https://esm.sh/matrix-engine-wgpu";
let RES = 'https://unpkg.com/matrix-engine-wgpu@latest/public';
  • Backend editorX (VisualScripting) (works in local env - desktop browsers) support list: Chrome, Edge, Opera, Firefox desktop

  • Code Creator (TheBeast Code generator) is standalone. Can be used public but user must trust page to enter api key. Without api key no ai response. Code creator make project from task and test it direct intro same page. Code creator dont even needed to build own js bundle, just use it from watcher build on localhost:3000 by default.


Done list []

  • ✔️ Draw loop per pipeline not per mesh (PipelineManager) with share material. Power optimisation.
  • ✔️ Support for 3D objects and scene transformations.
  • ✔️ Ammo.js, cannonES && Jolt Physics libs integration (app<->bridge<->worker). Physics use webworkers by default. Fixed crossOrigin worker.
  • ✔️ Networking with Kurento/OpenVidu/Own middleware Nodejs -> frontend.
  • ✔️ Bloom post processing.
  • ✔️ HZB post processing.
  • ✔️ Scene Culling feature (see maze example).
  • 📦 Based on the shadowMapping sample from webgpu-samples.
  • ✔️ Web GUI(online) Editor [app exec graph] with Visual Scripting (Named: FlowCodexVertex).
  • ✔️ Web GUI(online) Editor [shader graph] with Visual Scripting (Named: FlowCodexShader).
  • ✔️ Dynamic shadow cast (done also for skinned meshes).
  • ✔️ VertexShader displacment (done also for skinned meshes), nice for water effect.
  • ✔️ Basic flow for AI Graph Generator - Simple tasks passed for now with ollama platform. Open account/open-source/free-service-quota.
  • ✔️ ProceduralMesh objectScene entity with options for vertex morph - Shadows following morph blend.
  • ✔️ MediaPipe implemented - dinamic loading (if not in use no loading script or any related to mediapipe)
  • ✔️ Remote render stream emit example (standard engine networking) and special endpoint for "wachers" tv-10.html Replace webcam stream with canvas capture - on endpoint use webRTC and video tag to preview remote render.
  • ✔️ Code creator AI services agent system(use engine from npm) npm run creator for 'http://localhost:3000/' npm run creator-backend for backend. create games from simple prompt.

Roadmap

  • (Preparing API DOCs)[https://github.com/zlatnaspirala/matrix-engine-wgpu/wiki/Visual-Scripting-API]
  • 🎯 Test linux OS -> Editor creates and manages files internally.
  • 🎯 Improve gizmo feature.
  • 🎯 Sync npm version and make editor posible from npm i matrix-engine-wgpu.
  • 🎯 Sync npm version for matrix-engine-wgpu wrapper (me-webgpu-react)[https://github.com/zlatnaspirala/me-webgpu-react]
  • Add fully physics options. Soft bodies etc...

FluxCodexVertex Web Graph Editor 🚀 (since version 1.8.0)

EditorX has two main parts:

  • Frontend (./src/tools/editor)
  • Backend (./src/tools/editor/backend)

Before running anything, install dependencies with npm i:

  • in the root folder
  • and also inside ./src/tools/editor/backend

The backend is built using Node.js 🟢


General Features 🧩

  • Editor creates and manages files internally (Windows tested only!).
  • Scene container [adding objs -> auto save]
  • SceneObject property container [selected object] [auto save]
  • Assets toolbar added (bottom panel)
    • Add GLB or OBJ files (also mp3) from the asset toolbox by selecting them.
  • Top menu for adding primitives (Cube / Sphere) with or without physics ⚙️
  • Integrated Visual Scripting system 🧠 FluxCodexVertex for program exec flow.
  • Integrated Visual Scripting system 🧠 FluxCodexShader for shader.

Visual Scripting – Implemented Features ✅

  • Add Math nodes, events / custom methods, variable popup, SceneObject access
  • Get SceneObject → set position → bind onTargetReach events
  • SetTexture, setMaterial,
  • Fetch, GetArray, forEach, Print, IF, Math, compare, string operation etc...
  • Custom func editor - Function Manager after creating use it from visual scripting.
  • Generator physics bodies in sequence pos in choosen geometry in world pos (Pyramid, wall, in place).
  • onDraw Event node - called on every frame.Can be multiply used and set skip value. More skip less calls.
  • Audio reactive node Audio to pos , rot, scale or geometry or any... Outputs low, mid, high, energy and beat.
  • Run the graph ▶️
  • Stop the graph Just basic - clear dinamic created object and stops onDraw calls.
  • Save graph
    • Saved to file direct also cached on LocalStorage
    • For final builds, becomes a real JS object injected into the app flow.[DONE]
  • Export graph to JSON
  • Import graph from JSON
  • Insert graph from JSON from generated data (ai tool)
  • [Experimental] Generate graphs with ai tools services First working graph done with ollama platform. You need to open account on ollama platform for free quota service. Get API key and add it to the aitool config file (src\tools\editor\backend\config.js) Backend Config.js is in git ignored list. If you need to create it use this format: If not exist just create it (it's ignored).
    export const aiConfig = {
      ollama: "***********",
      qrok: "************",
      anthropic: "************",
      google: "************"
    };
    For now i create graphs with simple tasks. In future it will be able to create whole scenes or simple games.

⚠️ Important Notes

  • Visual Scripting is only available when running the engine from source
    (not from npm i matrix-engine-wgpu). You must clone or download the engine source from the GitHub repository.

Instructions 📌

Editor use esbuild compiler but still present browserify, both works perfect.

  • Run the editor with:
npm run editorx

from the engine root directory. EditorX is an alias for FluxCodexVertex backend part (needed three words to keep the name unique) Run the scene by pressing F6 or by clicking Run in the left panel If you delete all objects from the scene, you must refresh the page and add at least one object again Before importing a graph, delete all nodes from the FluxCodexVertex graph After deleting everything, click Save to store an empty [] array. All changes in graph must be saved manually/clicking for now 💾 (no autosave for graphs).


TheBeast Code Creator

Running Standalone (but it is buildin) use engine from npm service not from src. Running from the Engine Monorepo: If you cloned the full matrix-engine-wgpu repo, run these from the engine root folder:

npm run creator-install   # First time
# Or go manually:
# "cd src/tools/the-beast-code-gen 
# npm i

# Running
npm 
npm run creator          # frontend
npm run creator-backend  # backend (socket server)

API Keys: src\tools\the-beast-code-gen\src\config.js

From [1.18.6] version:

  • Added code-creator AI driven by not for graphs it is for code direct. Backend: npm run creator-backend Frontend: npm run creator

  • Code Creator part (no visual nodes - just top project code) can be public. Users must trust you! I will force local usage.

Api documentation

Build api with:

typedoc index.js

Live: https://maximumroulette.com/apps/webgpu/api-docs/

Features

Scene Management

  • Canvas is dynamically created in JavaScript—no <canvas> element needed in HTML.

  • Access the main scene objects:

    app.mainRenderBundle[0];

    or by name:

    app.getSceneObjectByName("Sphere1");
  • Add meshes with .addMeshObj(), supporting .obj loading, unlit textures, cubes, spheres, etc.

  • Destroy sceneObj:

    app.removeSceneObjectByName("Sphere1");
  • Also interest for clearing physics body and render part:

    app.destroyByPrefix("towers");
    // OR
    app.destroyBySufix("_001");
    // Destroy objects become true after calling flushDestroyQueue function.
    app.flushDestroyQueue();
  • Cleanly destroy the scene:

    app.destroyProgram();

Camera Options

Add in args for instancing main MEWGPU class.

mainCameraParams: {
  type: 'WASD',
  responseCoef: 1000
}

Access camera:

let cam = app.getCamera();
cam.setPitch(-0.26);
cam.setYaw(-0.06);
cam.setY(15);
cam.setZ(11);
cam._dirtyAngle = true;

For now translation is only with WASD keyboard keys. Supported types: WASD, RPGCamera, FirstPersonCamera, cinematicCamera

WASD also use 'c' and 'v' for up and down camera position.

To change position dont use direct edit on position array. Use methods:

maze.cameras.firstPersonCamera.movementSpeed = 0.1;
maze.collisionSystem.registerCamera(maze.cameras.firstPersonCamera.position, 1.0);
maze.cameras.firstPersonCamera.setPosition(-49, 10.40, -49);
maze.cameras.firstPersonCamera.setTarget(-49, 0.0, -49);

or

const cam = app.getCamera();
cam.setYaw(-0.03);
cam.setPitch(-0.49);
cam.setZ(0);
cam.setY(33);
cam._dirtyAngle = true;

Cinematic camera:

const introPath = new CameraPath([
  {position: [0, 5, 20], target: [0, 0, 0]},
  {position: [10, 12, 10], target: [0, 1, 0]},
  {position: [0, 15, -22], target: [0, 0, 0]},
], {parameterization: 'arc'});

if(cam.setPath) cam.setPath(introPath).play({
  speed: 0.3,
  onEnd: () => console.log('done'),
});

Object Position

Best way for access physics body object: app.matrixPhysics.getBodyByName(name) Return idx of body.Use that idx for any call of worker fn.

Control object position:

app.mainRenderBundle[0].position.translateByX(12);

Teleport / set directly:

app.mainRenderBundle[0].position.SetX(-2);

Adjust movement speed:

app.mainRenderBundle[0].position.thrust = 0.1;

⚠️ For physics-enabled objects, use matrixPhysics functions — .position and .rotation are not visually applied but can be read.

from 1.11.0 physics works from webworkers getBodyByName on cleaver way return body id not body inself but all funck now use ids not real body ref. interface stay same. see examples for physics like flipper game.

Example for apply physics on scene object click.

physicsPlayground.canvas.addEventListener("ray.hit.event", e => {
  console.log("ray.hit.event:", e.detail);
  const physics = app.matrixPhysics; // The engine instance
  const body = physics.getBodyByName(e.detail.hitObject.name);
  if (!body) return;
  // 1. Apply Impulse up
  // physics.applyImpulse(body, new PVector(0, 5, 0));
  // 2. Set Angular Velocity
  // physics.setAngularVelocity(body, new PVector(0, 9, 9));
  // 3. Directional hit based on ray
  // const dir = e.detail.rayDirection; // assuming [x, y, z]
  // const strength = 20;
  // physics.applyImpulse(body, new PVector(
  //   dir[0] * strength,
  //   dir[1] * strength,
  //   dir[2] * strength
  // ));
  // 4. Explosion example
  const hitPos = new PVector(
    e.detail.hitPoint.x,
    e.detail.hitPoint.y,
    e.detail.hitPoint.z,
  );
  physics.explode(idx, hitPos, 10, 50);
  // 5. Change Materials
  // const metal = {friction: 0.4, restitution: 0.1};
  // physics.setMaterial(body, metal.friction, metal.restitution);
});

Object Rotation

Manual rotation:

app.mainRenderBundle[0].rotation.x = 45;

Auto-rotate:

app.mainRenderBundle[0].rotation.rotationSpeed.y = 10;

Stop rotation:

app.mainRenderBundle[0].rotation.rotationSpeed.y = 0;

⚠️ For physics-enabled objects, use matrixPhysics methods (e.g., .applyImpulse).


💡 Lighting System

Matrix Engine WGPU now supports independent light entities, meaning lights are no longer tied to the camera. You can freely place and configure lights in the scene, and they will affect objects based on their type and parameters.

Supported Light Types Ambient global also per material SpotLight – Emits light in a cone shape with configurable cutoff angles.

(Planned: PointLight)

Features:

✅ Supports multiple lights ✅ Shadow-ready

## Important Required to be added manual:
engine.addLight();

Access lights with array lightContainer:

app.lightContainer[0];
app.getSceneLightByName("light1");

Small behavior object.

  • For now just one ocs0 object Everytime if called than updated (light.setPosX(light.behavior.setPath0())) behavior.setOsc0(min, max, step); app.lightContainer[0].behavior.osc0.onmaximum_value = function() {/ what ever*/}; app.lightContainer[0].behavior.osc0.on_minimum_value = function() {/* what ever_/};

Make light move by x.

loadObjFile.addLight();
loadObjFile.lightContainer[0].behavior.setOsc0(-1, 1, 0.01);
loadObjFile.lightContainer[0].behavior.value_ = -1;
loadObjFile.lightContainer[0].updater.push(light => {
  light.setPosX(light.behavior.setPath0());
  // setTargetX ...
});

Materials

With last glb feature materials become part of engine also.

material: {
  type: "standard";
}
material: {
  type: "pong";
}
material: {
  type: "power";
}
material: {
  type: "water";
}
material: {
  type: "metal";
}
material: {
  type: "mirror";
}
  • Standard is fully supported with lights shadow cast down (not for anims yet)
  • Pong
  • Power - no shadows cast
  • Mirror is env map. If you use mirror you must pass arg
  • Dark
// Also for addMeshObj
TEST_ANIM.addGlbObj({
  material: {type: 'power'},
...
}, null, glbFile);


// Mirror example
TEST_ANIM.addGlbObj({
  material: {type: 'mirror'},
  envMapParams: {
    baseColorMix: 0.55,
    mirrorTint: [0.9, 0.95, 1.0],     // Slight cool tint
    reflectivity: 0.95,               // 25% reflection blend
    illuminateColor: [0.3, 0.7, 1.0], // Soft cyan
    illuminateStrength: 0.1,          // Gentle rim
    illuminatePulse: 0.001,           // No pulse (static)
    fresnelPower: 5.0,                // Medium-sharp edge
    envLodBias: 2.5,
    usePlanarReflection: false,       // ✅ FOr now only false - Env map mode
  },
...
}, null, glbFile);
  • Mirror mat update params In runtime:
// Access some object with mirror mat
app.mainRenderBundle[5].writeParamsMirror({
  baseColorMix: 0.9,
  mirrorTint: [0.9, 0.95, 1.0],
  reflectivity: 0.25,
  illuminateColor: [1.3, 0.7, 1.0],
  illuminateStrength: 11.4,
  illuminatePulse: 1.1,
  fresnelPower: 2.5,
  envLodBias: 2.5,
  usePlanarReflection: false,
});
  • Change only textures (no recreation of pipeline)
await app.mainRenderBundle[0].loadTex0(["res/icons/editor/chatgpt-gen-bg.png"]);
app.mainRenderBundle[0].changeTexture(app.mainRenderBundle[0].texture0);
  • Setup Blend (For water mat use blend!)
app.mainRenderBundle[0].setBlend(0.5);

Examples for setup water params:

app.mainRenderBundle[0].updateWaterParams(
  [0.0, 0.1, 0.3], // Deep: navy
  [0.2, 0.6, 0.9], // Shallow: blue
  2.0, // Wave speed: fast continuous
  1.8, // Wave scale: rolling waves
  0.5, // Wave height: tall active waves
  1.5, // Fresnel: strong
  50.0, // Specular: soft highlights
);

app.mainRenderBundle[0].updateWaterParams(
  [0.0, 0.3, 0.5], // Deep: medium blue
  [0.3, 0.8, 1.0], // Shallow: bright cyan
  1.2,             // Wave speed: gentle continuous (changed from 0.6)
  2.5,             // Wave scale: smooth ripples (changed from 5.0)
  0.3,             // Wave height: visible movement
  2.5,             // Fresnel: moderate reflection
  100.0,           // Specular: sharp sparkles
);

Effect (Standalone pipeline draw subsystem)

Activation for prebuild(buildin) effects


TEST_ANIM.addGlbObj({
...
pointerEffect: {
  enabled: true,
  flameEffect: true,
  flameEmitter: true,
},
...
}, null, glbFile);

// Access in runtime :
// app.mainRenderBundle[0].effects

Use random for creation than when you find perfect crazzy fancy case just collect input data see: https://gist.github.com/zlatnaspirala/8758bcfb8be81d65c2c428e222abbef2

Bloom post processing

Activete with :

app.activateBloomEffect();

Manipulate with app.bloomPass: setKnee setIntensity setThreshold setBlurRadius

Fancy results:

Volumetric effect

Call from app app.activateVolumetricEffect() !Note volumetric works only if bloom is activated. Bloom can work alone.

Passing arguments:

app.activateVolumetricEffect({
  density: 0.03,
  steps: 32,
  scatterStrength: 1.2,
  heightFalloff: 0.08,
  lightColor: [1.0, 0.88, 0.65],
});

Object Interaction (Raycasting)

The raycast returns:

{
  rayOrigin: [x, y, z],
  rayDirection: [x, y, z] // normalized
}

Manual raycast example:

window.addEventListener("click", event => {
  let canvas = document.querySelector("canvas");
  let camera = app.cameras.WASD;
  // let camera = app.getCamera();
  const {rayOrigin, rayDirection} = getRayFromMouse(event, canvas, camera);

  for (const object of app.mainRenderBundle) {
    if (
      rayIntersectsSphere(
        rayOrigin,
        rayDirection,
        object.position,
        object.raycast.radius,
      )
    ) {
      console.log("Object clicked:", object.name);
    }
  }
});

Automatic raycast listener:

addRaycastListener();

// Must be app.canvas or [Program name].canvas
app.canvas.addEventListener("ray.hit.event", event => {
  console.log("Ray hit:", event.detail.hitObject);
});

Engine also exports (box):

  • addRaycastsAABBListener
  • rayIntersectsAABB,
  • computeAABB,
  • computeWorldVertsAndAABB,

How to Load .obj Models

import MatrixEngineWGPU from "./src/world.js";
import {downloadMeshes} from "./src/engine/loader-obj.js";

export let application = new MatrixEngineWGPU(
  {
    useSingleRenderPass: true,
    canvasSize: "fullscreen",
    mainCameraParams: {
      type: "WASD",
      responseCoef: 1000,
    },
  },
  () => {
    addEventListener("PhysicsReady", () => {
      downloadMeshes(
        {
          welcomeText: "./res/meshes/blender/piramyd.obj",
          armor: "./res/meshes/obj/armor.obj",
          sphere: "./res/meshes/blender/sphere.obj",
          cube: "./res/meshes/blender/cube.obj",
        },
        onLoadObj,
      );
    });

    function onLoadObj(meshes) {
      application.myLoadedMeshes = meshes;
      for (const key in meshes) {
        console.log(`%c Loaded obj: ${key} `, LOG_MATRIX);
      }
      application.addMeshObj({
        position: {x: 0, y: 2, z: -10},
        rotation: {x: 0, y: 0, z: 0},
        rotationSpeed: {x: 0, y: 0, z: 0},
        texturesPaths: ["./res/textures/cube-g1-extra_low.png"],
        name: "CubePhysics",
        mesh: meshes.cube,
        physics: {
          enabled: true,
          geometry: "Cube",
        },
      });

      application.addMeshObj({
        position: {x: 0, y: 2, z: -10},
        rotation: {x: 0, y: 0, z: 0},
        rotationSpeed: {x: 0, y: 0, z: 0},
        texturesPaths: ["./res/textures/cube-g1-extra_low.png"],
        name: "SpherePhysics",
        mesh: meshes.sphere,
        physics: {
          enabled: true,
          geometry: "Sphere",
        },
      });
    }
  },
);

window.app = application;

🔁 Load OBJ Sequence Animation

This example shows how to load and animate a sequence of .obj files to simulate mesh-based animation (e.g. walking character).

import MatrixEngineWGPU from "../src/world.js";
import {downloadMeshes, makeObjSeqArg} from "../src/engine/loader-obj.js";
import {LOG_MATRIX} from "../src/engine/utils.js";

export var loadObjsSequence = function () {
  let loadObjFile = new MatrixEngineWGPU(
    {
      useSingleRenderPass: true,
      canvasSize: "fullscreen",
      mainCameraParams: {
        type: "WASD",
        responseCoef: 1000,
      },
    },
    () => {
      addEventListener("PhysicsReady", () => {
        downloadMeshes(
          makeObjSeqArg({
            id: "swat-walk-pistol",
            path: "res/meshes/objs-sequence/swat-walk-pistol",
            from: 1,
            to: 20,
          }),
          onLoadObj,
          {scale: [10, 10, 10]},
        );
      });

      function onLoadObj(m) {
        console.log(`%c Loaded objs: ${m} `, LOG_MATRIX);
        var objAnim = {
          id: "swat-walk-pistol",
          meshList: m,
          currentAni: 1,
          animations: {
            active: "walk",
            walk: {from: 1, to: 20, speed: 3},
            walkPistol: {from: 36, to: 60, speed: 3},
          },
        };

        loadObjFile.addMeshObj({
          position: {x: 0, y: 2, z: -10},
          rotation: {x: 0, y: 0, z: 0},
          rotationSpeed: {x: 0, y: 0, z: 0},
          scale: [100, 100, 100],
          texturesPaths: ["./res/textures/cube-g1-extra_low.png"],
          name: "swat",
          mesh: m["swat-walk-pistol"],
          physics: {
            enabled: false,
            geometry: "Cube",
          },
          objAnim: objAnim,
        });

        app.mainRenderBundle[0].objAnim.play("walk");
      }
    },
  );

  window.app = loadObjFile;
};

💡 GLB binary loading bvh(rig)animations.

  • See examples glb-loader.js (build with npm run glb-loader)
  • Next update materials improvements!
  • Light affect just for first frame or t-pose.
  • For npm service import uploadGLBModel.

From 1.6.0 glb support multi skinned mesh + mutli primitives cases.

Limitation: glb loader not handled for non animation case. Use obj loader for static mesh.

Must powerfull call is new class MEMeshObjInstances. MEMeshObj is good now for optimised call(less conditionals). You can add instanced draws and modify basic color for each individualy also transformation good for fantazy or any game dev.

Example:

var glbFile01 = await fetch(p).then(res =>
  res.arrayBuffer().then(buf => uploadGLBModel(buf, this.core.device)),
);
this.core.addGlbObjInctance(
  {
    material: {type: "standard", useTextureFromGlb: true},
    scale: [20, 20, 20],
    position: {x: 0, y: -4, z: -220},
    name: this.name,
    texturesPaths: ["./res/meshes/glb/textures/mutant_origin.webp"],
    raycast: {enabled: true, radius: 1.5},
    pointerEffect: {enabled: true},
  },
  null,
  glbFile01,
);

// access  - index -0 is BASE MESH ! I added maxLimit = 5 you can change this from engine source.
// added lepr smoot translate , also color+.
app.mainRenderBundle[1].instanceTargets[1].position[2] = 10;
// This recreate buffer it is not for loop call space
app.mainRenderBundle[1].updateInstances(5);

ProceduralMesh

export const geoTypesForMorph = {
  cube: "cube",
  sphere: "sphere",
  mobius: "mobius",
  plane: "plane",
  capsule: "capsule",
  cone: "cone",
  torus: "torus",
  cylinder: "cylinder",
  wavePlane: "wavePlane",
  supershape: "supershape",
  pyramid: "pyramid",
  diamond: "diamond",
  icosahedron: "icosahedron",
  circlePlane: "circlePlane",
  rock: "rock",
  star: "star",
  star3d: "star3d",
  littleStar: "littleStar",
  flatStar: "flatStar",
  klein: "klein",
  shell: "shell",
  rippleSphere: "rippleSphere",
  twistedTorus: "twistedTorus",
  tornado: "tornado",
  galaxySpiral: "galaxySpiral",
};

Draw all buildin proceduralMesh shapes:

const spacing = 3;
const keys = Object.keys(geoTypesForMorph);
let col = 0;
let row = 0;
for (let i = 0; i < keys.length - 1; i++) {
  const typeA = keys[i];
  const typeB = keys[i + 1];
  loadObjFile.addProceduralMeshObj({
    material: {type: "power"},
    position: {x: col * spacing - 5, y: 1, z: -15 + row * spacing},
    rotation: {x: 0, y: 0, z: 0},
    scale: [1, 1, 1],
    rotationSpeed: {x: 0, y: 0, z: 0},
    texturesPaths: ["./res/textures/cube-g1_low.webp"],
    meshA: MeshMorpher[typeA](1),
    meshB: MeshMorpher[typeB](1),
    name: `morph_${typeA}_to_${typeB}`,
    physics: {
      enabled: false,
      geometry: "Sphere",
    },
  });
  col++;
  if (col % 4 === 0) {
    row++;
    col = 0;
  }
}

// Also works - but tested just two cube.
// need to improve and est more
// let test = MeshMorpher.compose(
//   {shape: MeshMorpher.cube(1), offset: [-2, 0, 0]},
//   {shape: MeshMorpher.cube(1), offset: [2, 0, 0]},
// );

// loadObjFile.addProceduralMeshObj({
//     material: {type: 'power'},
//     position: {x: 0 , y: 5, z: -15},
//     rotation: {x: 0, y: 0, z: 0},
//     scale: [1, 1, 1],
//     rotationSpeed: {x: 0, y: 0, z: 0},
//     texturesPaths: ['./res/textures/cube-g1_low.webp'],
//     meshA: test,
//     meshB: test,
//     name: `morph_1`,
//     physics: {
//       enabled: false,
//       geometry: "Sphere"
//     }
//   });

Morph between two shapes

sceneObject.morphTo(1.0, 2000, () => {
  /*callback*/
});

sceneObject.morphTo(0.0, 2000, () => {
  /*callback*/
});

📽️ Video textures

TEST.loadVideoTexture({
  type: "video", // video , camera  //not tested canvas2d, canvas2dinline
  src: "res/videos/tunel.mp4",
});

For canvasinline attach this to arg (example for direct draw on canvas2d and passing intro webgpu pipeline):

canvaInlineProgram: (ctx, canvas) => {
  ctx.fillStyle = "black";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.fillStyle = "white";
  ctx.font = "20px Orbitron";
  ctx.fillText(`FPS: ${Math.round(performance.now() % 60)}`, 10, 30);
};

Note

Warn can be disabled from MECongif.logLoopError (bool). If this happen less then 15 times (Loading procces) then it is ok probably...

Draw func (err):TypeError: Failed to execute 'beginRenderPass' on 'GPUCommandEncoder': The provided value is not of type 'GPURenderPassDescriptor'.

Note VideoTexture

It is possible for 1 or 2 warn in middle time when mesh switch to the videoTexture. Will be fixxed in next update.

Dimension (TextureViewDimension::e2DArray) of [TextureView of Texture "shadowTextureArray[GLOBAL] num of light 1"] doesn't match the expected dimension (TextureViewDimension::e2D).

Networking

From 1.7.0 engine powered by networking. Used kurento&Openvidu server for backend. Very good for handling streams, channels etc...

All you needed to start kurento and openvidu and interconnect this nodejs server https://github.com/zlatnaspirala/matrix-stream/blob/main/middleware/server.js

See example code at ./examples/games/moba/

Buildin net sync basic: Let's say app is engine root object and net is networking object. webRTC tech with openvidu server middleware server

app.net = new MatrixStream({
  active: true,
  domain: "maximumroulette.com",
  port: 2020,
  sessionName: "forestOfHollowBlood-free-for-all",
  resolution: "160x240",
  isDataOnly: urlQuery.camera || urlQuery.audio ? false : true,
  customData: forestOfHollowBlood.player.data,
});

// `customData` if you wanna pass some extra meta data on connection-created event
// No need always camera and mic we can use is like data signaling only.

How to use buildin network operations:

// Activate emiting remote position, on remote side adapted on scene object with same name
sceneObject.position.netObject = sceneObject[0].name;
// For now net view for rot is axis separated - cost is ok for orientaion remote pass
sceneObject.rotation.emitY = sceneObject.name;
// If you need oposite remote/local situation. For example:
// you friendly object is enemy object at remote machine that just setup another flag
sceneObject.position.netObject = sceneObject[0].name; // we still need this setup!
sceneObject.position.remoteName = sceneObjecOposite[0].name;

Use toRemote arg prop in send pass, if not it is default [] emit to all.

Intelegent emit for teams (two teams implemented). Position will be emited for teams[0] and received for uniq scene obj name. Position will be emited for teams[1] and received for oposite (eg. enemy) uniq scene obj name. In this case toRemote is overrided (Don't pass it). Used for moba project.

mesh.position.teams[0] = [connId0, connId1];
mesh.position.teams[1] = [connId2, connId3];

// emiter in core engine file
if (this.teams.length > 0)
  if (this.teams[0].length > 0)
    app.net.send({
      toRemote: this.teams[0], // default null remote conns
      sceneName: this.netObject, // origin scene name to receive
      netPos: {x: this.x, y: this.y, z: this.z},
    });
if (this.teams.length > 0)
  if (this.teams[1].length > 0)
    app.net.send({
      toRemote: this.teams[1], // default null remote conns
      remoteName: this.remoteName, // to enemy players
      sceneName: this.netObject, // now not important
      netPos: {x: this.x, y: this.y, z: this.z},
    });

About URLParams

Buildin Url Param check for multiLang. MultiLang feature is also buildin options.

Load multilang json file data.

  • ?lang=en

Access from code:

urlQuery.lang;

Also render config can be controlled:

Use it : matrix-engine-wgpu/public/examples?demo=1&SHADOW_RES=250

If you wanna full performance on mobile devices than use in constructor: This is width of canvas inself. Value 1 is the best quality case also default if not exist.

fastRender = from 0.1 to 1 - Best for mobile 0.85

In this case FORCE_FULL_SCREEN is true by default. Pushed notification click anywhere to start the engine.

Note for MAX_SPOTLIGHTS : If you wanna override values from MECOnfig or even urlParam than use in main engine arg pass:

new MatrixEngineWGPU({
    canvasSize: 'fullscreen',
    fastRender: 0.9,
    render: 'culling',
    dontUsePhysics: true,
    MAX_SPOTLIGHTS: 1,
    MAX_BONES: 0,
    mainCameraParams: {
      type: 'firstPersonCamera',
      // type: 'WASD',
      responseCoef: 1000
    },
    clearColor: {r: 0, b: 0.122, g: 0.122, a: 1}
  }, () => { ... })

Exclude (for example floor/ground) from culling: floor.ignoreCulling = true;

About empty.js

[Not always updated] For a clean startup without extra logic, use engine build empty.js. This minimal build is ideal for online editors like CodePen or StackOverflow snippets. It is the plain text JavaScript ready for exec on any platform with zero errors.


Android TV or any other supported device render stream receiver

Tech:L webRTC (used standard kurento/openvidu matrixStream) Look for ./public/tv-10.html

To make or build you own see android native part at: https://github.com/zlatnaspirala/web-to-native/tree/master/android-tv At thie repo (cef) you can find way to port also on windows, macos or linux OS but this not priory for project. Dev help for android studio: "adb connect 192.168.0.XX:5555"

We need somewhere to run standard render and use networking just with new flag (streamRender):

  streamRender.net = new MatrixStream({
    active: true,
    domain: 'maximumroulette.com',
    port: 2020,
    sessionName: 'tv-beast',
    resolution: '1920x1080',
    isDataOnly: false,
    streamRender: true
  });

NPM Scripts

Editor use esbuild. Uses watchify to bundle JavaScript. (still good) Slow migration to the ESBUILD (note: it strange becouse it is exe file).

You can setup more games/apps in single bundle or make special end point for specific game/app.

"examples": "esbuild examples.js --bundle --outfile=public/examples.js --format=esm --watch",
"android-tv-cast": "esbuild android-tv-cast.js --bundle --outfile=public/android-tv-cast.js --format=esm --watch",
"jamb": "watchify ./examples/games/jamb/jamb.js -p [esmify --noImplicitAny] -o public/yacht.js",
"demo1": "watchify demo1.js -p [esmify --noImplicitAny] -o public/app.js",
"empty": "watchify empty.js -p [esmify --noImplicitAny] -o public/empty.js",
"build.jamb": "browserify main.js -p esmify > public/app.js",
"glb-loader": "watchify ./examples/glb-loader.js -p [esmify --noImplicitAny] -o public/app.js",
"moba": "watchify ./examples/games/moba/forestOfHollowBlood.js -p [esmify --noImplicitAny] -o public/moba-game.js",
"moba-menu": "watchify ./examples/games/moba/start-menu.js -p [esmify --noImplicitAny] -o public/moba-menu.js",
"build-all": "npm run examples | npm run main | npm run empty",
"creator-install": "npm --prefix src/tools/the-beast-code-gen i",
"creator": "npm --prefix src/tools/the-beast-code-gen run dev",
"creator-backend": "npm --prefix src/tools/the-beast-code-gen run backend",
"landpage": "watchify ./examples/my-lights.js -p [esmify --noImplicitAny] -o public/landpage.js",
"hosts": "http-server ./public/ -p 3000",
"editor-landing": "watchify ./src/tools/editor/template/autogen.js -p [esmify --noImplicitAny] -o public/matrix-engine-autogen.js",
"editorx": "cd src/tools/editor/backend && node matrix-editor.js"

Resources

All resources and output go into the ./public folder — everything you need in one place. This is static file storage also all builds make outputs here.

  • For adding new endpoint app/demo you only need to create standard html file with js script name.

Proof of Concept

🎲 The first full app example will be a WebGPU-powered Jamb 3d deluxe game. Also MOBA 'Forest of hollow blood'

MOBA game Beta version done

Features done:

  • Supporetd on mobile browsers.
  • Navigation mesh
  • Hero class
  • GLB animations
  • Automatic team selection (South vs North)
  • Homebase stone (tron / enemytron)
  • Invertory (construct from 2 or 3 new upgraded item)
  • Videochat - stream video follow player character (remote & local 'one on one')

Install it on your desktop with one click.

💲💲💲 Invest in Forest Of Hollow Blood 1,000$ See more details at FOHB Wiki


Live Demos & Dev Links

Codepen examples:

CodeSandbox


Performance for Jamb game:

Special licence for MOBA example:

Creative Commons Attribution 4.0 International (CC BY 4.0)

You are free to share and adapt this project, provided that you give appropriate credit.
Attribution requirement:
Include the following notice (with working link) in any distributed version or about page:

"Forest Of Hollow Blood — an MOBA example made with MatrixEngineWGPU (https://github.com/zlatnaspirala/matrix-engine-wgpu)"

Web Editor FluxCodexVertex from version [1.8.0]

Run editor

npm run editorx

Navigate to matrix-engine.html it is landing page for editor. After create new project or load project page will be redirect to ./public/<PROJECT_NAME>.html Source location : ./projects/<PROJECT_NAME>

Features :

  • Create new project/ load project - only on landing page

  • Create cubeMesh

  • Properties box for selected sceneObj

  • Events system (Create func and attach to sceneObj)

  • Resource navigation

  • Visual Scripting

@Note License for fluxCodexVertex.js (MPL 2.0) Affect only this file! Just leave comment licence part in header of file.

YT video promotion :

About 'In fly' regime: Editor can be activated even without backend node but in that case no saves.

License && Credits

Usage Note

You may use, modify, and sell projects based on this code — just keep this notice and included references intact (whole licence paragraph - but you can always remove assets -> remove asset back link).

  • Most important is reference on matrix-engine-wgpu.
  • You need just to copy paste this text to about form on your web page or any other type of app.
  • You can remove almost all licence for assets if you remove current assest from your project (Fork this repo - remove all required link back assets - remove assets licence)

Attribution & Credits

  • Engine design and scene structure inspired by: WebGPU Samples

  • OBJ Loader adapted from: http://math.hws.edu/graphicsbook/source/webgl/cube-camera.html

  • Dice roll sound roll1.wav sourced from: https://wavbvkery.com/dice-rolling-sound/

  • Raycasting logic and glb loader assisted by ChatGPT.

  • GLTF Loader: https://github.com/Twinklebear/webgpu-gltf, improved with chatgpt.

  • Origin jpg (free format) source: http://spiralgraphics.biz/ More: Https://blenderartists.org/t/is-spiral-graphics-genetica-dead/1446058

  • https://polyhaven.com/a/checkered_pavement_tiles

  • Music by Mykola Sosin from Pixabay

  • Characters used from great mixamo.com -✅What you can do You can use Mixamo characters and animations royalty-free in commercial, personal, or non‑profit projects (games, films, prints, etc.).You own your creations / how you use them.No requirement to credit Adobe / Mixamo (though allowed). -🚫What you cannot do You cannot redistribute or sell the raw Mixamo character or animation files “as is” (i.e. as standalone assets) to others.You can’t use Mixamo content to create a competing library of characters / animations (i.e. you can’t just package them and sell them to others). You can’t use Mixamo’s content (or outputs) to train AI / machine learning models.

  • Music used: BLACK FLY by Audionautix | http://audionautix.com Music promoted by https://www.free-stock-music.com Creative Commons Attribution-ShareAlike 3.0 Unported

  • Used free assets from great https://craftpix.net Magic icons : https://craftpix.net/freebies/free-rpg-splash-game-512x512-icons/

  • For Hang3d zombi template - objects are downloaded from: www.md2.sitters-electronics.nl Keep this "readme.md" file with files.

  • For background music in rpg template used: Music by Dvir Silverstone from Pixabay Sound Effect by Crunchpix Studio from Pixabay Music by Emmraan from Pixabay

    'Ruined rock fence' (https://skfb.ly/6RLwN) by VladNeko is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).

    "fantasy rock" (https://skfb.ly/oHXAz) by duckcracker02 is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).

    "Fantasy Rock" (https://skfb.ly/oHZSq) by lalune is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).

    Invertory images https://djinnbestiary.itch.io/ancient-oddities-vol-1-13-free-potions

    "Dancing girl" (https://skfb.ly/p7vNU) by planetrey.com is licensed under Creative Commons Attribution (http://creativecommons.org/licenses/by/4.0/).

    Skeletal system by: Aidan Sanderson - from blender addson blenderKit.

    Title: Zombies Sound Pack - Author: artisticdude Source URL: https://opengameart.org/content/zombies-sound-pack License: CC0 1.0 Universal (Public Domain Dedication)

    Folder './public/res/bvh/' Special thanks to the CMU Graphics Lab Motion Capture Database which provided the data http://mocap.cs.cmu.edu/


BSD 3-Clause License (from WebGPU Samples)

Full License Text

Top level main.js instance (Jamb 3d deluxe)


📘 Learning Resource:

WebGPU Ray Tracing

  • ChatGPT, claude and Gemini.