Build a game with an LLM
nape-js ships machine-readable docs, so Claude, ChatGPT, Copilot and Cursor can write against it without guessing. What they still get wrong is the handful of places where this engine differs from Box2D and Matter.js — the prompts below front-load exactly those, so you spend your time on the game instead of on debugging invented API calls.
1. Give the model the docs
Start every session with this. It points the assistant at the full API reference in a single file — roughly 76 KB, well within a modern context window — so it stops inferring method names from other engines.
I'm building a 2D game with @newkrok/nape-js, a TypeScript physics engine. Before writing any code, read the full API reference: https://raw.githubusercontent.com/NewKrok/nape-js/master/packages/nape-js/llms-full.txt Key rules for this engine, which differ from Box2D/Matter.js: - Bodies join a world by assignment: `body.space = space` (there is no `space.add()`). - Shapes attach the same way: `body.shapes.add(new Circle(20))`. - There is NO `body.applyForce()`. Assign `body.force = new Vec2(x, y)` instead, and clear it each frame — it persists across `space.step()`. - `body.applyImpulse(vec)` DOES exist and is the usual way to push things. - Gravity is a Vec2 on the Space: `new Space(new Vec2(0, 600))`. Y points down. - Step with a fixed timestep: `space.step(1 / 60)`. Use TypeScript. Ask me before inventing any API you did not see in that file.
.cursorrules, CLAUDE.md, or
.github/copilot-instructions.md in your project and it applies
to every request automatically.
2. Pick a starting point
Each of these assumes the primer above is already in context. They are deliberately specific about the physics setup, because that is the part models improvise badly.
Platformer
Build a side-scrolling platformer with nape-js and a canvas renderer. Requirements: - Use the built-in `CharacterController` helper for the player rather than hand-rolling ground checks — it already handles slopes, coyote time, one-way platforms, and moving-platform inheritance. - Level geometry: static bodies with `Polygon.box()` shapes. - A camera that follows the player, updated ONLY on physics steps (not on every animation frame) so movement doesn't stutter on 120Hz displays. - Fixed timestep with an accumulator; render interpolation between steps. Note: `CharacterController` ORs a bit into the body's collision filter group, so if I later add projectiles, account for that when setting up filtering.
Top-down / arena
Build a top-down arena game with nape-js. No gravity: `new Space(new Vec2(0, 0))`. Requirements: - Player and AI agents are `Circle` shapes (circles steer and slide cleanly; boxes snag on corners). - Set `body.allowRotation = false` on agents so they don't spin on contact. - Movement by setting `body.velocity` directly, not by applying force. - Walls as static bodies around the play area. - Simple AI: steer toward a target, with a dead zone around the facing angle so agents don't jitter between left and right when nearly aligned. - Detect "is A touching B" by polling `body.interactingBodies()` each step and calling `.clear()` on the returned list — simpler than an InteractionListener when you only need a boolean.
Physics puzzle
Build a physics puzzle game with nape-js where the player drags objects to solve each level. Requirements: - Mouse dragging via a `PivotJoint` between a static "hand" body and the grabbed body — create it on mousedown, remove it on mouseup. - At least one level using a `DistanceJoint` rope and one using a `PulleyJoint`. - A win condition checked with an `InteractionListener` on a sensor shape. - Soft joints where a rope or spring rests against the floor: set `stiff = false` with a frequency around 12 and damping 1. A fully rigid joint fights the floor contact and jitters.
Deterministic multiplayer
Build a server-authoritative multiplayer prototype with nape-js. Requirements: - Server runs the simulation with `space.deterministic = true` and a fixed timestep; clients render interpolated snapshots. - Use `@newkrok/nape-js/serialization` (`spaceToJSON` / `spaceFromJSON`) for the initial world sync, then send per-frame deltas for bodies whose position or rotation actually changed. - Set `body.isBullet = true` on fast-moving bodies to enable CCD. Read the multiplayer guide before designing the protocol: https://github.com/NewKrok/nape-js/blob/master/docs/guides/multiplayer-guide.md Determinism here is same-platform only — floating-point results differ across CPU architectures, so don't design for cross-platform lockstep.
3. Mistakes models reliably make
These come from real sessions. Paste the relevant one when you hit it — or add them to your project rules up front.
A Polygon falls straight through the floor
Polygon constructed with an explicit
Material can tunnel through static polygon floors —
isBullet does not help.
Fix: omit the material argument and let the engine use
its default (new Polygon(Polygon.box(w, h))), or use a
Circle or Capsule, which are unaffected.
Most likely to bite in car and vehicle demos, where both the chassis and
the track walls are polygons.
Invented method: body.applyForce()
body.force = new Vec2(x, y).
The value persists across steps, so zero it each frame
unless you want it to accumulate. body.applyImpulse(vec)
is a real method and is usually what you actually want.
Rotating a shape doesn't rotate its collision geometry
shape.rotation changes nothing about how the shape
collides. Rotate the body instead:
body.rotation = Math.PI / 2.
Reading space.arbiters throws "Index out of bounds"
space.arbiters.at(0) throws when the list is empty, and
.length is undefined. Get the count with
space.arbiters.zpp_gl() and guard before indexing. Note a
sleeping body reports zero arbiters, so use
body.isSleeping as a fallback in ground checks.
Material with density 0 throws a mass error
new Material(elasticity, dynamicFriction, staticFriction, density).
A density of 0 yields zero mass on a dynamic body and
errors out. Models often pass 0 meaning "default".
An AngleJoint does nothing inside its range
jointMin < jointMax the joint is a pure range limit:
no force at all while the angle sits inside the window. For a spring
that always pulls toward a target angle, set
jointMin === jointMax.
The camera stutters at 120Hz but the physics looks fine
4. What to hand the model next
Beyond the primer, these are the files worth pasting or linking when a task goes deeper.