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 🙏

© 2025 – Pkg Stats / Ryan Hefner

dom-physics

v0.1.7

Published

A simple, performant physics engine for DOM elements. Preserves DOM structure while enabling realistic physics simulation.

Readme

DOM Physics Engine

A simple, performant physics engine for DOM elements. Preserves DOM structure while enabling realistic physics simulation. Built with TypeScript.

🌐 Live Demos on GitHub Pages | 📦 npm

Why This Package?

Simple & Fast

  • No complex nesting - Simple Body and World classes
  • Direct physics - No inheritance chains or recursive lookups
  • Optimized collision detection - Simple O(n²) for small counts, efficient for most use cases
  • Minimal overhead - Only manipulates CSS transform properties

What Changed from Complex Version?

The previous version had advanced features like:

  • World extending Body (nesting)
  • Physics inheritance with recursive lookups
  • SpatialHash optimization (removed - not needed for typical use cases)
  • Complex coordinate transformations

Why we simplified:

  1. Performance - Recursive lookups (getEffectiveGravity(), getWorldPosition()) were called every frame, causing O(n) complexity
  2. Simplicity - Most use cases don't need nesting - a simple World with Bodies is enough
  3. Reliability - Fewer moving parts = fewer bugs
  4. Maintainability - Easier to understand and modify

Result: The simplified version matches the original demo's performance exactly while being much easier to use and understand.

Features

DOM Structure Preserved - Never modifies DOM hierarchy, only manipulates transforms
Simple API - Just World and Body classes
Framework Agnostic - Works with vanilla JS, React, Vue, etc.
TypeScript - Full type safety and IntelliSense support
Performant - Optimized for 60fps with many bodies

Installation

npm install dom-physics

Quick Start

import { World, Body } from 'dom-physics';

// Create a world
const container = document.getElementById('world');
const world = new World(container, {
  gravity: 400,
  friction: 0.97,
  restitution: 0.5
});

// Create a body
const element = document.querySelector('.my-element');
const body = new Body(element, world, {
  mass: 1,
  radius: 15,
  restitution: 0.8
});

// Register and start
world.registerBody(body);
world.start();

Demos

🌐 Try all demos live on GitHub Pages

Main Demo (Published Demo)

Text Demo (demo-text.html) - Interactive text that responds to mouse movement. This is the main published demo showcasing the package.

Additional Demos

  • Squares Demo (demo-squares.html) - Click to add squares, hover to push them around. Demonstrates collision detection and bounds.
  • Bouncing Balls (demo-bouncing.html) - Colorful balls bouncing in a circular container. Shows high restitution physics.
  • Stack Demo (demo-stack.html) - Build towers by clicking. Watch blocks stack and balance with realistic physics.

Running Demos Locally

Option 1: Using npm (if installed as package)

# Install the package
npm install dom-physics

# Navigate to package directory
cd node_modules/dom-physics

# Run the demo server
npm run demo:package

# Open http://localhost:8087/demo-package/ in your browser
# Navigate to specific demos:
# - http://localhost:8087/demo-package/demo-text.html (main demo)
# - http://localhost:8087/demo-package/demo-squares.html
# - http://localhost:8087/demo-package/demo-bouncing.html
# - http://localhost:8087/demo-package/demo-stack.html

Option 2: From Source (if developing)

# Clone the repository
git clone https://github.com/kai4avaya/dom-physics.git
cd dom-physics

# Install dependencies
npm install

# Build the package
npm run build

# Run the demo server
npm run demo:package

# Open http://localhost:8087/demo-package/ in your browser

Option 3: Original Inline Demo

# Run the original inline demo (for comparison)
npm run demo

# Open http://localhost:8087/ in your browser

API Reference

World

class World {
  constructor(container: HTMLElement, config?: WorldConfig)
  
  // Simulation control
  start(): void
  stop(): void
  
  // Body management
  registerBody(body: Body): void
  unregisterBody(body: Body): void
  
  // Properties
  container: HTMLElement
  bodies: Body[]
  gravity: number
  friction: number
  restitution: number
  timeStep: number
  bounds: { x: number; y: number; width: number; height: number }
}

Body

class Body {
  constructor(
    element: HTMLElement,
    world: World,
    config?: BodyConfig
  )
  
  // Physics control
  applyForce(fx: number, fy: number): void
  
  // Position queries
  getWorldPosition(): { x: number; y: number }
  
  // Rendering
  render(): void
  
  // Properties
  element: HTMLElement
  world: World
  x: number
  y: number
  mass: number
  radius: number
  restitution: number | null
  friction: number | null
  isStatic: boolean
  enabled: boolean
}

Configuration

WorldConfig

interface WorldConfig {
  gravity?: number;        // px/s² (default: 980)
  friction?: number;       // 0-1 (default: 0.99)
  restitution?: number;    // 0-1 (default: 0.8)
  bounds?: {               // Default: auto-detect from container
    x: number;
    y: number;
    width: number;
    height: number;
  };
  timeStep?: number;       // seconds (default: 1/60)
}

BodyConfig

interface BodyConfig {
  mass?: number;           // Default: 1
  radius?: number;          // Default: auto-calculated from element size
  restitution?: number | null;  // null = use world's restitution
  friction?: number | null;     // null = use world's friction
  isStatic?: boolean;      // Default: false
}

Examples

Basic Usage

const world = new World(container, {
  gravity: 400,
  friction: 0.97,
  restitution: 0.5
});

const body = new Body(element, world, {
  mass: 1,
  radius: 15
});

world.registerBody(body);
world.start();

Mouse Interaction

container.addEventListener('mousemove', (e) => {
  const rect = container.getBoundingClientRect();
  const mx = e.clientX - rect.left;
  const my = e.clientY - rect.top;
  
  world.bodies.forEach(body => {
    const pos = body.getWorldPosition();
    const dx = pos.x - mx;
    const dy = pos.y - my;
    const dist = Math.sqrt(dx * dx + dy * dy);
    
    if (dist < 100 && dist > 0) {
      const force = (100 - dist) * 2;
      body.applyForce(
        (dx / dist) * force,
        (dy / dist) * force
      );
    }
  });
});

Custom Physics Properties

// Body uses world's friction and restitution
const body1 = new Body(element1, world);

// Body overrides restitution
const body2 = new Body(element2, world, {
  restitution: 0.9  // Bouncier than world default
});

// Body overrides friction
const body3 = new Body(element3, world, {
  friction: 0.95  // Less friction than world default
});

Principles

  1. DOM Structure Preserved - Never modifies DOM hierarchy
  2. Transform Only - Only manipulates transform CSS property
  3. World Space Simulation - Bodies simulate in world space, render relative to DOM
  4. Simple & Fast - No unnecessary complexity

Testing

npm test              # Run tests in watch mode
npm run test:run      # Run tests once
npm run test:coverage # Run tests with coverage

License

MIT