Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e9e445d
feat: migrate to pnpm monorepo with tsdown and fix all package variants
alexandernanberg Jan 31, 2026
f23d928
feat: upgrade to rapier 0.32 with glam migration
alexandernanberg Jan 31, 2026
9bf79c0
perf: use call4 instead of bind2+call2 in physics hooks
alexandernanberg Jan 31, 2026
7a7c5b0
chore: upgrade wasm-pack to 0.14.0
alexandernanberg Jan 31, 2026
4ab1e19
fix: resolve all Rust/Cargo warnings
alexandernanberg Jan 31, 2026
2a3e907
perf: add zero-allocation getters for hot path properties
alexandernanberg Jan 31, 2026
aa1e188
feat: add benchmarks package for performance testing
alexandernanberg Jan 31, 2026
c7cfe28
docs: update CLAUDE.md and README with benchmarks and zero-alloc getters
alexandernanberg Jan 31, 2026
0cab496
chore: re-enable LTO in release profile
alexandernanberg Jan 31, 2026
5ed8068
feat(testbed2d): upgrade pixi.js to 8.x
alexandernanberg Jan 31, 2026
e888ae5
feat(testbed3d): upgrade three.js to 0.182 and fix lighting
alexandernanberg Jan 31, 2026
54c96f3
chore: upgrade tsdown to 0.20.1
alexandernanberg Jan 31, 2026
f04af93
chore: upgrade typedoc to 0.28.16
alexandernanberg Jan 31, 2026
fc34bab
chore: migrate to oxfmt
alexandernanberg Jan 31, 2026
6c7a101
chore: remove dim 2/3 blocks
alexandernanberg Jan 31, 2026
f301034
chore: fix TypeScript strict null check errors
alexandernanberg Jan 31, 2026
3e7cf5f
perf: cache gravity
alexandernanberg Jan 31, 2026
a9efe59
chore: add oxlint
alexandernanberg Jan 31, 2026
02229d3
perf: add target parameter to TempContactManifold vector methods
alexandernanberg Jan 31, 2026
2ff00df
fix: update pkg name
alexandernanberg Jan 31, 2026
0a5786f
perf: pass ray primitives directly to WASM for ray casting
alexandernanberg Jan 31, 2026
525cbeb
docs: update README benchmarks and baseline
alexandernanberg Feb 1, 2026
99fddf3
docs: expand README with detailed performance explanations
alexandernanberg Feb 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"enabledPlugins": {
"claude-md-management@claude-plugins-official": true,
"code-review@claude-plugins-official": true,
"ralph-loop@claude-plugins-official": true,
"typescript-lsp@claude-plugins-official": true
}
}
21 changes: 21 additions & 0 deletions .oxfmtrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"experimentalSortPackageJson": true,
"experimentalSortImports": {
"groups": [
["type-import", "value-builtin", "value-external"],
["type-internal", "value-internal"],
["type-parent", "type-sibling", "type-index"],
["value-parent", "value-sibling", "value-index"],
"unknown"
],
"sortSideEffects": false,
"partitionByNewline": false,
"newlinesBetween": false
},
"tabWidth": 4,
"bracketSpacing": false,
"trailingComma": "all",
"printWidth": 100,
"ignorePatterns": ["pkg", "gen2d", "gen3d", "docs", "target", "dist", "wasm", "crates"]
}
17 changes: 17 additions & 0 deletions .oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": [],
"categories": {
"correctness": "off",
"suspicious": "off"
},
"rules": {
"no-unused-vars": "error"
},
"settings": {},
"env": {
"builtin": true
},
"globals": {},
"ignorePatterns": []
}
7 changes: 0 additions & 7 deletions .prettierignore

This file was deleted.

5 changes: 0 additions & 5 deletions .prettierrc

This file was deleted.

3 changes: 1 addition & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
{
"javascript.format.enable": false,
"typescript.format.enable": false
"editor.defaultFormatter": "oxc.oxc-vscode"
}
35 changes: 17 additions & 18 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,24 +168,23 @@ See [parry#336](https://github.com/dimforge/parry/pull/336) for additional infor
- Update to Rapier 0.24.
- Package tree shaking has been disabled to avoid a crash on certain build configuration (#289).
- Multiple packages with different feature sets are now released, and their build process has been automated (#309)
- Released packages are:
- rapier2d
- rapier2d-compat
- rapier2d-simd
- rapier2d-simd-compat
- rapier2d-deterministic
- rapier2d-deterministic-compat
- rapier3d
- rapier3d-compat
- rapier2d
- rapier2d-compat
- rapier3d-simd
- rapier3d-simd-compat
- rapier3d-deterministic
- rapier3d-deterministic-compat
- :warning: To this occasion, existing packages `rapier2d` and `rapier3d` are now built without `enhanced-determinism` feature enabled,
so if you rely on that feature, you should migrate to the new `-deterministic` flavor.

- Released packages are:
- rapier2d
- rapier2d-compat
- rapier2d-simd
- rapier2d-simd-compat
- rapier2d-deterministic
- rapier2d-deterministic-compat
- rapier3d
- rapier3d-compat
- rapier2d
- rapier2d-compat
- rapier3d-simd
- rapier3d-simd-compat
- rapier3d-deterministic
- rapier3d-deterministic-compat
- :warning: To this occasion, existing packages `rapier2d` and `rapier3d` are now built without `enhanced-determinism` feature enabled,
so if you rely on that feature, you should migrate to the new `-deterministic` flavor.

### 0.14.0 (20 July 2024)

Expand Down
219 changes: 219 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
# CLAUDE.md - AI Assistant Context for rapier.js

## Project Overview

This is a **fork** of rapier.js - TypeScript bindings for the Rapier physics engine (Rust → WASM).

## Prerequisites

- Node.js 24+
- pnpm (`npm install -g pnpm`)
- Rust toolchain (`rustup`)
- wasm-pack (`cargo install wasm-pack`)

- **Package scope**: `@alexandernanberg/rapier-{2d,3d}`
- **Monorepo**: pnpm workspaces
- **Stack**: Rust + wasm-bindgen → WASM, TypeScript bindings

### Performance Goals

1. Minimize JS↔WASM boundary crossings
2. Avoid temporary object allocations in hot paths
3. Prefer getters over methods for simple computed values

## Repository Structure

```
crates/
rapier-wasm-2d/ # WASM crate for 2D (uses shared src/)
rapier-wasm-3d/ # WASM crate for 3D (uses shared src/)

packages/
rapier-2d/ # TypeScript bindings for 2D physics
rapier-3d/ # TypeScript bindings for 3D physics
testbed2d/ # 2D demo application
testbed3d/ # 3D demo application
```

## Package Variants

Each package (`rapier-2d`, `rapier-3d`) ships 4 variants:

| Import Path | WASM Loading | SIMD |
| ---------------------------- | -------------------- | ---- |
| `@.../rapier-2d` | `fetch()` at runtime | No |
| `@.../rapier-2d/simd` | `fetch()` at runtime | Yes |
| `@.../rapier-2d/compat` | Embedded base64 | No |
| `@.../rapier-2d/compat-simd` | Embedded base64 | Yes |

**Usage**:

- Default/SIMD: Best for web apps (smaller bundle, parallel loading)
- Compat variants: For environments without `fetch()` (SSR, workers, tests)

## Build Commands

```bash
pnpm build # Full build (WASM + TypeScript)
pnpm build:wasm # All WASM variants
pnpm build:ts # TypeScript packages only
pnpm build:2d # 2D only (WASM + TS)
pnpm build:3d # 3D only (WASM + TS)
pnpm typecheck # Type check all packages
pnpm fmt # Format code with oxfmt
pnpm dev:testbed2d # Run 2D demo
pnpm dev:testbed3d # Run 3D demo
```

## Benchmarks

Run performance benchmarks to measure physics engine performance:

```bash
pnpm bench # Full 3D benchmark
pnpm bench:2d # Full 2D benchmark
pnpm bench:quick # Quick mode (fewer iterations)
```

**Benchmark categories:**

- **Simulation**: `world.step()` performance with stacked bodies
- **Lifecycle**: Body creation/destruction throughput
- **Queries**: Ray casting and point projection performance
- **Getters**: Property access with/without allocation

Results are saved to `packages/benchmarks/results/` as timestamped JSON files.

## Critical Memory Management Patterns

### Rule 1: Always `init()` Before API Use

```typescript
import RAPIER from "@alexandernanberg/rapier-2d";

await RAPIER.init(); // REQUIRED before any API calls
const world = new RAPIER.World({x: 0, y: -9.81});
```

### Rule 2: Free Raw Objects After `intoRaw()` Calls

When passing data to WASM, temporary raw objects must be freed:

```typescript
// CORRECT - free raw objects after use
let rawOrig = VectorOps.intoRaw(ray.origin);
let rawDir = VectorOps.intoRaw(ray.dir);

let result = this.raw.castRay(rawOrig, rawDir, maxToi);

rawOrig.free(); // REQUIRED
rawDir.free(); // REQUIRED

return result;
```

```typescript
// WRONG - memory leak
let rawOrig = VectorOps.intoRaw(ray.origin);
let rawDir = VectorOps.intoRaw(ray.dir);
return this.raw.castRay(rawOrig, rawDir, maxToi);
// rawOrig and rawDir are never freed!
```

### Rule 3: `fromRaw()` Auto-Frees, `intoRaw()` Does Not

- `fromRaw(raw)`: Consumes and frees the raw object automatically
- `intoRaw()`: Returns raw object that YOU must free

### Rule 4: Free World/Controller Resources

Classes with `raw` property need explicit cleanup:

```typescript
class KinematicCharacterController {
public free() {
if (!!this.raw) {
this.raw.free();
}
this.raw = undefined;
}
}
```

## Zero-Allocation Getters

For hot paths, use the optional `target` parameter to avoid allocations:

```typescript
// Allocating (creates new object each call)
const pos = body.translation();

// Zero-allocation (reuses existing object)
const _pos = {x: 0, y: 0, z: 0};
body.translation(_pos); // writes into _pos
```

Supported methods: `translation()`, `rotation()`, `linvel()`, `angvel()`,
`nextTranslation()`, `nextRotation()`, `localCom()`, `worldCom()` on RigidBody,
and `translation()`, `rotation()` on Collider.

## 2D vs 3D Differences

| Concept | 2D | 3D |
| -------- | ------------------------------------------ | ---------------------------------- |
| Rotation | `number` (radians) | `Quaternion` `{x,y,z,w}` |
| Vector | `{x, y}` | `{x, y, z}` |
| Shapes | Ball, Cuboid, Capsule, ConvexPolygon, etc. | + ConvexPolyhedron, Cylinder, Cone |

## Common Pitfalls

1. **Memory leaks**: Missing `.free()` after `intoRaw()` calls
2. **Uninitialized WASM**: Calling API before `await init()`
3. **Wrong rotation type**: Using quaternion in 2D or number in 3D
4. **Stale handles**: Using `RigidBodyHandle` after body removed from world

## Key Type Patterns

### Handles vs Objects

```typescript
// Handle = lightweight reference (number)
type RigidBodyHandle = number;
type ColliderHandle = number;

// Get actual object from set using handle
const body = world.getRigidBody(handle);
```

### Descriptor Pattern

```typescript
// Use descriptors to configure before creation
const bodyDesc = RAPIER.RigidBodyDesc.dynamic().setTranslation(0, 5);

const body = world.createRigidBody(bodyDesc);
```

## File Navigation Hints

| Looking for... | Check |
| -------------------- | ------------------------------------------------------------- |
| Shape definitions | `packages/rapier-{2d,3d}/src/geometry/shape.ts` |
| Rigid body API | `packages/rapier-{2d,3d}/src/dynamics/rigid_body.ts` |
| World/simulation | `packages/rapier-{2d,3d}/src/pipeline/world.ts` |
| Collision detection | `packages/rapier-{2d,3d}/src/geometry/narrow_phase.ts` |
| Ray/shape casting | `packages/rapier-{2d,3d}/src/geometry/broad_phase.ts` |
| Character controller | `packages/rapier-{2d,3d}/src/control/character_controller.ts` |
| WASM init logic | `packages/rapier-{2d,3d}/src/init.ts`, `init-compat.ts` |
| Math utilities | `packages/rapier-{2d,3d}/src/math.ts` |

## Testing

Testbeds serve as integration tests:

```bash
pnpm dev:testbed2d # http://localhost:5173
pnpm dev:testbed3d # http://localhost:5173
```

Demo files in `packages/testbed{2d,3d}/src/demos/` show usage patterns.
Loading
Loading