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

@tangent.to/mc

v0.4.0

Published

JavaScript Markov Chain Monte Carlo - A PyMC-inspired probabilistic programming library for Bayesian inference in JavaScript

Readme

@tangent/mc - JavaScript Markov Chain Monte Carlo

A PyMC-inspired probabilistic programming library for Bayesian inference in JavaScript. Built on TensorFlow.js with automatic differentiation support for efficient MCMC sampling.

Overview

MC brings the power of Bayesian statistical modeling to JavaScript, providing an intuitive API similar to PyMC for defining probabilistic models as Directed Acyclic Graphs (DAGs) and performing inference using Markov Chain Monte Carlo methods.

API conventions

MC follows the same API conventions as its sibling data-science package @tangent.to/ds:

  • Namespaced + flat exports. Import individual symbols (import { Normal } from '@tangent.to/mc'), grouped namespaces (import { distributions, samplers } from '@tangent.to/mc'), or the whole library as a default export (import mc from '@tangent.to/mc'mc.distributions.Normal). The namespaces are distributions, samplers, diagnostics, io, and plot.
  • Options-object constructors. Every configurable class accepts a single options object in addition to positional arguments, e.g. new Normal({ mean: 0, sd: 1 }) or new MetropolisHastings({ proposalStd: 0.5 }). Positional forms continue to work.
  • Introspection. Distributions and samplers expose getParams().

Key Features

  • PyMC-like DAG structure: Define models by connecting distributions in a directed acyclic graph
  • TensorFlow.js integration: Automatic differentiation for gradient-based samplers
  • Multiple MCMC samplers: Metropolis-Hastings and Hamiltonian Monte Carlo
  • Rich distribution library: Normal, Uniform, Beta, Gamma, Bernoulli, and more
  • Posterior predictions: Generate predictions with uncertainty from MCMC samples
  • Model persistence: Save and load traces and model configurations to JSON
  • Trace analysis utilities: Summary statistics, effective sample size, convergence diagnostics
  • Hierarchical models: Support for multilevel Bayesian models
  • Browser compatible: Run in Node.js or in the browser (including ObservableHQ)

Installation

@tangent.to/mc ships a single browser-first build and uses TensorFlow.js (@tensorflow/tfjs) for tensor math and automatic differentiation. tfjs is a peer dependency — it is not bundled into mc, so you load it once and share it (mixing two tfjs copies breaks tensor interop). See Loading TensorFlow.js below.

Node.js / npm

npm install @tangent.to/mc @tensorflow/tfjs
import { Model, Normal, MetropolisHastings } from '@tangent.to/mc';

Deno

import { Model, Normal, MetropolisHastings } from "npm:@tangent.to/mc";

Loading TensorFlow.js

How you provide tfjs depends on whether you use a build step:

With a bundler (Vite, webpack, esbuild, …) — install both packages and import as usual; the bundler resolves @tensorflow/tfjs for you. Nothing else to do.

Without a build step (plain <script type="module">, CDN) — bare imports don't resolve in the browser, so add an import map before importing mc:

<script type="importmap">
{
  "imports": {
    "@tensorflow/tfjs": "https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@4/+esm"
  }
}
</script>
<script type="module">
  import { Model, Normal, MetropolisHastings } from 'https://cdn.jsdelivr.net/npm/@tangent.to/mc/+esm';
  // ... build and sample your model
</script>

mc also re-exports the shared instance, so you can grab tf from mc itself instead of importing it separately:

import { tf } from '@tangent.to/mc';

Observable

jsDelivr's +esm endpoint auto-resolves the tfjs dependency, so a single import works:

mc = import("https://cdn.jsdelivr.net/npm/@tangent.to/mc/+esm")

Quick Start

Here's a simple Bayesian linear regression example:

import { Model, Normal, Uniform, MetropolisHastings, printSummary } from '@tangent.to/mc';

// Create model
const model = new Model({ name: 'linear_regression' });

// Define priors (options-object form; positional `new Normal(0, 10, 'alpha')` also works)
const alpha = new Normal({ mean: 0, sd: 10, name: 'alpha' });
const beta = new Normal({ mean: 0, sd: 10, name: 'beta' });
const sigma = new Uniform({ min: 0.01, max: 5, name: 'sigma' });

model.addVariable('alpha', alpha);
model.addVariable('beta', beta);
model.addVariable('sigma', sigma);

// Define likelihood (connecting distributions in a DAG)
model.logProb = function(params) {
  let logProb = alpha.logProb(params.alpha)
    .add(beta.logProb(params.beta))
    .add(sigma.logProb(params.sigma));

  // Add likelihood for observations
  for (let i = 0; i < x.length; i++) {
    const mu = params.alpha + params.beta * x[i];
    const likelihood = new Normal(mu, params.sigma);
    logProb = logProb.add(likelihood.logProb(y[i]));
  }

  return logProb;
};

// Run MCMC sampling (options-object form; positional args also work)
const sampler = new MetropolisHastings({ proposalStd: 0.5 });
const trace = sampler.sample(model, initialValues, { nSamples: 1000, burnIn: 500, thin: 1 });

// Analyze results
printSummary(trace);

Namespaced / default imports

import mc from '@tangent.to/mc';

const model = new mc.Model({ name: 'linear_regression' });
model.addVariable('alpha', new mc.distributions.Normal({ mean: 0, sd: 10, name: 'alpha' }));

const sampler = new mc.samplers.MetropolisHastings({ proposalStd: 0.5 });
// mc.diagnostics, mc.io, mc.plot are also available

Core Concepts

Models as DAGs

Like PyMC, mc uses a Directed Acyclic Graph (DAG) structure to represent probabilistic models. Variables can depend on other variables, creating a natural flow from priors through transformations to likelihoods:

// Hyperpriors
const mu_global = new Normal(0, 10);
const sigma_global = new Uniform(0, 5);

// Group-level parameters (depend on hyperpriors)
const mu_group = new Normal(mu_global, sigma_global);

// Observations (depend on group parameters)
const y = new Normal(mu_group, sigma_obs);

Distributions

mc provides a rich set of probability distributions:

Each constructor accepts positional arguments or an options object (shown second).

Continuous Distributions

  • Normal: new Normal(mu, sigma) / new Normal({ mean, sd }) - Gaussian distribution
  • Uniform: new Uniform(lower, upper) / new Uniform({ min, max }) - Uniform distribution
  • Beta: new Beta(alpha, beta) / new Beta({ alpha, beta }) - Beta distribution (for probabilities)
  • Gamma: new Gamma(alpha, beta) / new Gamma({ shape, rate }) - Gamma distribution (for positive values)

Discrete Distributions

  • Bernoulli: new Bernoulli(p) / new Bernoulli({ p }) - Binary outcomes

All distributions support:

  • logProb(value) - Compute log probability density/mass
  • pdf(value) - Compute probability density/mass (exp(logProb))
  • sample(shape) - Generate random samples
  • mean() - Get the distribution mean
  • variance() - Get the distribution variance
  • getParams() - Get the distribution's parameters as a plain object

Model Predictions

Generate posterior predictive samples for new data:

// Define prediction function
const predictFn = (params) => {
  return params.alpha + params.beta * x_new;
};

// Get posterior predictions with uncertainty
const predictions = model.predictPosteriorSummary(
  trace,
  predictFn,
  credibleInterval=0.95
);
// Returns: { mean: [...], lower: [...], upper: [...] }

Model Persistence

File-based persistence is Node-only (node:fs) and is intentionally kept out of the browser-first main entry. Import it directly from its module in Node:

import {
  saveTrace,
  loadTrace,
  saveModelState,
  exportTraceForBrowser
} from '@tangent.to/mc/persistence';

// Save trace to JSON
saveTrace(trace, 'trace.json');

// Load trace
const loadedTrace = loadTrace('trace.json');

// Save complete model state
saveModelState(model, trace, 'model_state.json');

// In the browser, serialize to a JSON string instead (no filesystem)
const jsonString = exportTraceForBrowser(trace);

MCMC Samplers

Metropolis-Hastings

A simple but effective random-walk sampler:

const sampler = new MetropolisHastings({ proposalStd });
const trace = sampler.sample(model, initialValues, { nSamples, burnIn, thin });

Parameters:

  • proposalStd: Standard deviation of the Gaussian proposal distribution
  • nSamples: Number of samples to collect
  • burnIn: Number of initial samples to discard
  • thin: Keep every nth sample

Best for: Simple models, initial exploration

Hamiltonian Monte Carlo

A gradient-based sampler that uses automatic differentiation:

const sampler = new HamiltonianMC({ stepSize, nSteps });
const trace = sampler.sample(model, initialValues, { nSamples, burnIn, thin });

Parameters:

  • stepSize: Leapfrog integration step size (epsilon)
  • nSteps: Number of leapfrog steps (L)

Best for: Complex models with many parameters, faster convergence

Trace Analysis

mc provides utilities for analyzing MCMC samples:

import { summarize, effectiveSampleSize, gelmanRubin, printSummary } from '@tangent.to/mc';

// Print comprehensive summary
printSummary(trace);

// Get statistics for a variable
const stats = summarize(trace.trace.alpha);
// Returns: { mean, median, std, variance, hdi_2_5, hdi_97_5, n }

// Compute effective sample size
const ess = effectiveSampleSize(trace.trace.alpha);

// Check convergence with multiple chains
const rHat = gelmanRubin([chain1.alpha, chain2.alpha, chain3.alpha]);

Examples

The examples/ directory contains complete working examples:

Linear Regression

node examples/linear_regression.js

Demonstrates basic Bayesian linear regression with normal priors.

Logistic Regression

node examples/logistic_regression.js

Binary classification with a logistic link function.

Hierarchical Model

node examples/hierarchical_model.js

Multilevel model with partial pooling across groups, showcasing complex DAG structures.

API Reference

Model Class

const model = new Model(name)

Methods:

  • addVariable(name, distribution, observed) - Add a variable to the model
  • getVariable(name) - Retrieve a variable
  • logProb(params) - Compute log probability
  • logProbAndGradient(params) - Compute log prob and gradients
  • samplePrior(nSamples) - Sample from prior distributions
  • getFreeVariableNames() - Get unobserved variable names
  • summary() - Print model structure

Distribution Classes

All distributions inherit from the base Distribution class:

class Distribution {
  logProb(value)      // Log probability
  pdf(value)          // Probability density/mass (exp of logProb)
  sample(shape)       // Generate samples
  observe(data)       // Set observed data
  mean()             // Distribution mean
  variance()         // Distribution variance
  getParams()        // Parameters as a plain object
}

Sampler Classes

Constructors and sample() accept either positional arguments or a single options object.

class MetropolisHastings {
  constructor(proposalStd)                 // or ({ proposalStd })
  sample(model, initialValues, nSamples, burnIn, thin)  // or (model, init, { nSamples, burnIn, thin })
  tuneProposal(acceptanceRate)
  getParams()
}

class HamiltonianMC {
  constructor(stepSize, nSteps)            // or ({ stepSize, nSteps })
  sample(model, initialValues, nSamples, burnIn, thin)
  getParams()
}

class NUTS {
  constructor(stepSize, maxTreeDepth, targetAcceptance)  // or ({ stepSize, maxTreeDepth, targetAcceptance })
  sample(model, initialValues, nSamples, nWarmup, thin)
  getParams()
}

Browser & ObservableHQ

mc is a single browser-first build that runs the same in the browser, Node, and ObservableHQ — see Installation for loading tfjs. In Observable:

mc = import("https://cdn.jsdelivr.net/npm/@tangent.to/mc/+esm")

{
  const { Model, Normal, MetropolisHastings } = mc;
  // ... define and run your model
}

Notes for browser/Observable use:

  • tfjs runs on its CPU/WebGL backend (there is no @tensorflow/tfjs-node — the single build uses @tensorflow/tfjs everywhere), which enables interactive visualization.
  • File-based persistence (saveTrace, loadTrace) is Node-only (node:fs) and is not part of the browser entry. Import it from the @tangent.to/mc/persistence subpath in Node if needed; in the browser, serialize with traceToJSON(trace).

Technical Details

Built on TensorFlow.js

mc leverages TensorFlow.js for:

  • Automatic differentiation: Essential for gradient-based samplers like HMC/NUTS
  • Efficient tensor operations: Fast computation of log probabilities
  • WebGL acceleration: GPU-backed tensor math in the browser via the WebGL backend

Comparison with PyMC

| Feature | PyMC | mc | |---------|------|------| | Language | Python | JavaScript | | Backend | Aesara/JAX | TensorFlow.js | | DAG Structure | Yes | Yes | | MCMC Samplers | NUTS, HMC, MH | HMC, MH | | Variational Inference | Yes | Planned | | GPU Support | Yes | Browser only (TF.js WebGL) |

Performance Tips

  1. Tune sampler parameters:

    • MH: Aim for 20-40% acceptance rate by adjusting proposalStd
    • HMC: Start with small stepSize (~0.01) and moderate nSteps (~10)
  2. Use appropriate burn-in: Discard at least 500-1000 initial samples

  3. Check convergence:

    • Visual inspection of trace plots
    • R-hat < 1.1 for multiple chains
    • Effective sample size > 100 per chain
  4. Hierarchical models: Use HMC for faster convergence with many parameters

Development

# Clone repository
git clone https://github.com/tangent-to/mc.git
cd mc

# Install dependencies
npm install

# Run examples
npm run example

# Run tests
npm test

Contributing

Contributions are welcome! Please feel free to submit issues and pull requests.

License

Apache-2.0

Roadmap

Completed in v0.2.0:

  • [x] Posterior predictive sampling
  • [x] Model persistence (save/load)
  • [x] Browser/Observable support

Planned:

  • [ ] Additional distributions (Poisson, Student-t, Exponential)
  • [ ] Variational inference (ADVI)
  • [ ] Model comparison utilities (WAIC, LOO)
  • [ ] Trace visualization tools
  • [ ] PyMC model import/export

Documentation

References

Citation

If you use mc in your research, please cite:

@software{mc,
  title = {mc: JavaScript Markov Chain Monte Carlo},
  author = {},
  year = {2025},
  url = {https://github.com/tangent-to/mc}
}