dom-physics
v0.1.7
Published
A simple, performant physics engine for DOM elements. Preserves DOM structure while enabling realistic physics simulation.
Maintainers
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
BodyandWorldclasses - 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
transformproperties
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:
- Performance - Recursive lookups (
getEffectiveGravity(),getWorldPosition()) were called every frame, causing O(n) complexity - Simplicity - Most use cases don't need nesting - a simple World with Bodies is enough
- Reliability - Fewer moving parts = fewer bugs
- 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-physicsQuick 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.htmlOption 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 browserOption 3: Original Inline Demo
# Run the original inline demo (for comparison)
npm run demo
# Open http://localhost:8087/ in your browserAPI 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
- DOM Structure Preserved - Never modifies DOM hierarchy
- Transform Only - Only manipulates
transformCSS property - World Space Simulation - Bodies simulate in world space, render relative to DOM
- 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 coverageLicense
MIT
