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

mathbox-next

v0.1.3

Published

Lightweight TypeScript math visualization library built on three.js r185

Readme

mathbox-next

Lightweight 3D math visualization library built on three.js r185 and TypeScript. Inspired by MathBox, but rewritten from scratch with a simplified architecture — no threestrap, no ShaderGraph, no data-as-texture GPU pipeline, no legacy three.js APIs.

Why?

MathBox is a powerful library, but it was built on three.js r71 (2014) and depends on threestrap and ShaderGraph. Running it alongside modern three.js (r160+) causes:

  • ESM resolution errors — directory imports without /index.js break Node's ESM resolver
  • WebGL1Renderer not found — removed in three.js r163
  • window.THREE global pollution — the bundle overwrites window.THREE, conflicting with any other three.js usage on the same page
  • Internal API breakage — renderer.state, renderer.properties, deprecated uniform type strings

mathbox-next sidesteps all of this by using standard ESM imports of modern three.js directly. No globals, no patches, no SSR workarounds.

Install

npm install mathbox-next three
# or
yarn add mathbox-next three

three is a peer dependency — you bring your own version (≥0.160.0).

Quick Start

import { MathBoxController, createDefaultScene } from "mathbox-next";

const container = document.getElementById("container")!;
const scene = createDefaultScene();

const controller = new MathBoxController(container, scene);
controller.start();

// Update the scene (adds/removes/updates objects via diffing)
controller.updateScene(updatedScene);

// Clean up
controller.dispose();

Scene Model

The scene is a plain JSON-serializable object — store it, send it over the wire, round-trip it through an editor.

interface MathScene {
    version: 1;
    config: {
        coordinateSystem: "cartesian" | "polar" | "spherical";
        axes: {
            x: { visible: boolean; range: [number, number]; scale: number; label?: string };
            y: { ... };
            z: { ... };
        };
        grid: { visible: boolean };
        camera: {
            canRotate: boolean;
            canZoom: boolean;
            canPan: boolean;
            position: [number, number, number];
            lookAt: [number, number, number];
        };
    };
    parameters: Parameter[];   // slider variables referenced in expressions
    objects: SceneObject[];    // visual elements
}

Object Types

| Type | Description | Key fields | |---|---|---| | function | 2D curve y = f(x) | expr, domain, samples, width | | surface | 3D surface z = f(x, y) | expr, domainX, domainY, samplesX, samplesY | | parametric | 3D curve (x(t), y(t), z(t)) | exprX, exprY, exprZ, domain, samples | | point | Point in 3D space | position, size | | line | Line segment between two points | start, end, width | | vector | Arrow from tail to head | tail, head, width |

Expression Syntax

Expressions are JavaScript math expressions. All Math.* functions are available as bare names:

"sin(x)"
"x^2 + 2*x - 1"          // ^ is rewritten to **
"sin(x) * cos(y)"
"sqrt(x^2 + y^2)"
"exp(-t) * cos(2*t)"

Parameters defined in scene.parameters are available as variables:

const scene = {
    // ...
    parameters: [
        { id: "p1", symbol: "a", value: 2, min: 0, max: 5, step: 0.1 },
    ],
    objects: [
        {
            id: "fn1",
            type: "function",
            visible: true,
            color: "#3b82f6",
            opacity: 1,
            showLabel: false,
            expr: "a * sin(x)",   // references parameter "a"
            domain: [-5, 5],
            samples: 200,
            width: 3,
            closed: false,
        },
    ],
};

Example: Full Scene

const scene: MathScene = {
    version: 1,
    config: {
        coordinateSystem: "cartesian",
        axes: {
            x: { visible: true, range: [-5, 5], scale: 1, label: "x" },
            y: { visible: true, range: [-3, 3], scale: 1, label: "y" },
            z: { visible: true, range: [-3, 3], scale: 1, label: "z" },
        },
        grid: { visible: true },
        camera: {
            canRotate: true,
            canZoom: true,
            canPan: true,
            position: [4, 3, 6],
            lookAt: [0, 0, 0],
        },
    },
    parameters: [],
    objects: [
        {
            id: "surface1",
            type: "surface",
            visible: true,
            color: "#10b981",
            opacity: 0.8,
            showLabel: false,
            expr: "sin(x) * cos(y)",
            domainX: [-3, 3],
            domainY: [-3, 3],
            samplesX: 60,
            samplesY: 60,
        },
        {
            id: "vec1",
            type: "vector",
            visible: true,
            color: "#8b5cf6",
            opacity: 1,
            showLabel: false,
            tail: [0, 0, 0],
            head: [2, 2, 1],
            width: 2,
        },
    ],
};

API

MathBoxController

The top-level orchestrator. Manages the three.js renderer, scene graph, camera, lights, and animation loop.

const controller = new MathBoxController(container, scene);

| Method | Description | |---|---| | start() | Begin the requestAnimationFrame render loop. | | stop() | Stop the render loop. | | updateScene(scene) | Replace the scene definition. Diffs objects — adds new, updates changed, disposes removed. | | updateParameters(params) | Update parameter values (e.g. from slider input). Refreshes affected renderers. | | dispose() | Stop loop and dispose all GPU resources. |

MathScene / Types

All types are exported from the package root:

import type { MathScene, SceneObject, FunctionObject, SurfaceObject, ... } from "mathbox-next";

Extending

Custom Object Type

Register a new scene object type by implementing ObjectRenderer and registering it:

import { ObjectRenderer, registerRenderer } from "mathbox-next";
import * as THREE from "three";

class SphereRenderer extends ObjectRenderer<MySphereObject> {
    private mesh: THREE.Mesh;

    update(obj: MySphereObject): void {
        // Build or update geometry from obj
    }

    refresh(obj: MySphereObject): void {
        // Called when parameters change but the object definition hasn't
    }

    needsRefresh(): boolean { return true; }

    setVisible(visible: boolean): void {
        this.root.visible = visible;
    }

    setResolution(w: number, h: number): void { /* ... */ }

    dispose(): void {
        this.mesh.geometry.dispose();
        (this.mesh.material as THREE.Material).dispose();
    }
}

registerRenderer("sphere", (parent, evaluator, scope, resolution) =>
    new SphereRenderer(parent, evaluator, scope, resolution));

Custom Evaluator

The default SimpleEvaluator uses new Function for expression evaluation. For untrusted input, swap in a sandboxed parser:

import type { Evaluator, EvalScope } from "mathbox-next";

class MathJSEvaluator implements Evaluator {
    eval(expr: string, scope: EvalScope): number {
        // Use math.js, expr-eval, or any safe parser
        return mathjs.evaluate(expr, scope);
    }
}

License

MIT