https://github.com/darkroomengineering/elastica
Physics engine for 2D rigid body collisions with React bindings.
https://github.com/darkroomengineering/elastica
aabb collision javascript-library react
Last synced: 3 months ago
JSON representation
Physics engine for 2D rigid body collisions with React bindings.
- Host: GitHub
- URL: https://github.com/darkroomengineering/elastica
- Owner: darkroomengineering
- Created: 2024-07-29T13:25:26.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2026-01-30T18:42:25.000Z (4 months ago)
- Last Synced: 2026-02-08T06:13:17.593Z (4 months ago)
- Topics: aabb, collision, javascript-library, react
- Language: TypeScript
- Homepage: https://elastica.darkroom.engineering
- Size: 131 MB
- Stars: 38
- Watchers: 4
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: readme.md
Awesome Lists containing this project
README
# Elastica
Physics engine for 2D rigid body collisions with React bindings.
[Demo](https://elastica.darkroom.engineering/)
## Features
- **Two collision modes**: AABB (axis-aligned) and OBB (oriented/rotational)
- **Two rendering modes**: DOM (CSS transforms) and Canvas (2D batched rendering)
- **Shape support**: Rectangles and circles
- **Frame-rate independent**: Fixed timestep physics with accumulator
- **Spatial hashing**: Efficient broad-phase collision detection
- **Configurable borders**: Rigid (bounce) or periodic (wrap-around)
## Installation
```bash
npm i @darkroom.engineering/elastica
```
## Quick Start
### DOM Mode
```tsx
import {
DomElastica,
BoundaryBox,
initalConditionsPresets,
updatePresets,
} from '@darkroom.engineering/elastica/react'
function App() {
return (
{items.map((item, i) => (
{item.name}
))}
)
}
```
### Canvas Mode
```tsx
import {
CanvasElastica,
CanvasBox,
initalConditionsPresets,
updatePresets,
} from '@darkroom.engineering/elastica/react'
function App() {
return (
{particles.map((p, i) => (
))}
)
}
```
## API
### DomElastica / CanvasElastica
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `config` | `object` | See below | Physics configuration |
| `initialCondition` | `function` | `() => {}` | Called once on mount to set initial positions/velocities |
| `update` | `function` | `() => {}` | Called each physics step to apply forces |
| `showHashGrid` | `boolean` | `false` | Visualize spatial hash grid (debug) |
### Config Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `collisions` | `boolean` | `true` | Enable collision detection |
| `borders` | `'rigid'` \| `'periodic'` \| `false` | `'rigid'` | Border behavior |
| `gridSize` | `number` | `8` | Spatial hash cell size (should be >= largest element) |
| `useOBB` | `boolean` | `false` | Enable rotational physics (OBB mode) |
| `containerOffsets` | `object` | `{ top: 0, bottom: 0, left: 0, right: 0 }` | Inset simulation bounds |
| `defaultMass` | `number` | `1` | Default mass for elements |
| `defaultRestitution` | `number` | `0.8` | Default bounciness (0-1) |
| `solver.fixedDeltaTime` | `number` | `16.67` | Physics timestep in ms |
| `solver.substeps` | `number` | `1` | Physics substeps per frame |
### BoundaryBox (DOM)
Wraps DOM elements for physics simulation.
```tsx
Content
```
### CanvasBox (Canvas)
Defines particle data for canvas rendering.
```tsx
```
### Hooks
```tsx
// Access engine instance and context
const { elastica, addBox, removeBox } = useDomElastica()
const { elastica, registerParticle, unregisterParticle } = useCanvasElastica()
// Control playback
const ref = useRef(null)
ref.current?.pause()
ref.current?.play()
```
### Update Callback
The `update` function receives physics state each frame:
```tsx
function customUpdate({
boxes, // Element data array
positions, // [x, y] positions (center)
velocities, // [vx, vy] velocities
externalForces, // Apply forces here
deltaTime, // Fixed timestep
angles, // Rotation angles (OBB mode)
angularVelocities,
masses,
bounced, // Collision flags
isStatic,
}: UpdateParams) {
boxes.forEach((_, index) => {
if (isStatic[index]) return
const velocity = velocities[index]
const position = positions[index]
// Apply gravity
velocity[1] += 0.001 * deltaTime
// Integrate position
position[0] += velocity[0] * deltaTime
position[1] += velocity[1] * deltaTime
})
}
```
## Presets
```tsx
import {
initalConditionsPresets,
updatePresets,
} from '@darkroom.engineering/elastica/react'
// Initial conditions
initalConditionsPresets.random // Random positions and velocities
initalConditionsPresets.randomOBB // Random with rotation (OBB mode)
// Update functions
updatePresets.dvdScreenSaver // Constant velocity, bounce on edges
updatePresets.dvdScreenSaverOBB // With rotation
updatePresets.DragAndGravity // Gravity + drag interaction
updatePresets.rightFlow // Horizontal flow field
```
## Engine Only
Use the physics engine without React:
```ts
import Elastica from '@darkroom.engineering/elastica'
const engine = new Elastica({
collisions: true,
borders: 'rigid',
useOBB: true,
})
engine.setContainer({ width: 800, height: 600 })
// Physics loop
function tick() {
engine.update(elements, (instance) => {
// Apply forces, integrate positions
})
requestAnimationFrame(tick)
}
```
## License
MIT