Skip to content
AI Primer
workflow

IMPULSE runs 20,000 physics bodies at 120fps in a browser

Linus Ekenstam presents IMPULSE, a Rust physics engine running through WebAssembly with a WebGL2 renderer. He reports an 83KB binary handling 20,000 bodies at 120fps on an M5 MacBook Pro.

3 min read
IMPULSE runs 20,000 physics bodies at 120fps in a browser
IMPULSE runs 20,000 physics bodies at 120fps in a browser

TL;DR

Ekenstam floated IMPULSE as a benchmark for future models. He also added a crude water plugin and wired in DialKit, a live-tuning interface library.

Wrecking balls, bridges and marble runs

Product designer Linus Ekenstam built IMPULSE after a couple of weeks testing Gemini 4 Argon. His brief was gloriously nerdy: a lightweight, reusable 3D engine with its own physics solver.

He demonstrated five setups in a later reel:

  • A wrecking ball.
  • A bridge simulator.
  • A marble run.
  • A water simulation.
  • Physics objects attached to strings.

IMPULSE's wrecking ball, bridge, marble run and water demonstrations

Collision detection and joints

The initial architecture fits into roughly 1,200 lines of Rust. Rather than incorporating Rapier or Bullet, Ekenstam describes four custom physics components:

  • Rotational dynamics: analytical world-space inverse inertia tensors, using R × I_local^-1 × Rᵀ.
  • Box collisions: a 15-axis Separating Axis Theorem test for oriented-box contact manifolds.
  • Contact resolution: iterative sequential impulses with Baumgarte stabilization.
  • Joints: a 3×3 effective mass matrix for ball-and-socket and catenary bridge joints.

WebGL2 shadows and mouse dragging

Ekenstam skipped wgpu in favor of direct WebGL2 access through web-sys, citing compilation speed and binary size. The renderer includes:

  • Shadows: two-pass 2048×2048 depth shadow mapping with 3×3 percentage-closer filtering.
  • Geometry: procedural unit meshes with local-space rotational stripes.
  • Interaction: screen-to-world ray unprojection for mouse-controlled spring dragging.
  • Debugging: toggleable axis-aligned bounding boxes and contact-normal wireframes.

Spatial hashing and four draw calls

The initial stress test used more than 500 rigid bodies with lighting and shadows. At roughly 2,100 bodies, Ekenstam hit a GPU limit and asked the model to optimize the engine.

He subsequently reported roughly 20,000 drawn bodies and 2,000 simultaneously colliding bodies after three changes:

  • Spatial hashing replaced all-pairs collision checks.
  • Instanced rendering reduced body rendering to at most four draw calls, including shadows.
  • Reusable buffers reduced allocations.

WebGL2 instancing executes multiple copies of geometry in one draw call, as described in MDN's API reference.

The expanded build was described as around 90KB. Ekenstam also demonstrated roughly 10,000 simulated bodies falling toward collisions in a separate scene.

The expanded stress test, labeled 20,000 bodies and 2,000 collisions

GitHub and playable access

Further reading

Discussion across the web

Where this story is being discussed, in original context.

On X· 3 threads
TL;DR5 posts
Wrecking balls, bridges and marble runs3 posts
Spatial hashing and four draw calls4 posts
Share on X