# Friction

# Friction

Package `@ferret/physics-friction`, capability name `friction`, phase `strength` (order −20: it acts before `impact` and before breakage).

## What it is for

The kernel already applies Coulomb friction from the **materials** of the two surfaces. This capability adds what a *game* dials around that: a patch of ice in one room, a puck that has no grip, a ball that rolls farther on a polished floor, a skate that slides along its blade and bites sideways. Materials describe stuff; `friction` describes places and moods.

## The model

At each contact the friction coefficients come from the material pair (materials (skill `ferret-physics-bodies`)); then this capability multiplies them by the scoped values at the contact (the dynamic body's entity, and the contact point, so a **zone** applies):

| Value | Default | Effect |
|---|---|---|
| `scale` | 1 | multiplies static and kinetic friction. 0 = frictionless, 0.05 = ice, 2 = tar |
| `rollingScale` | 1 | multiplies rolling resistance |
| `pivot` | the material's | pivot (spinning about the contact normal) friction coefficient |
| `anisotropy` | none | `{ axis: [x,y,z], across }`: across the axis the coefficient is `across` times the one along it |

Kinetic friction defaults to 0.8 × the static coefficient when the material gives only the static one. A sliding body slows at `μk · g` (on flat ground).

## First use

```ts
import { PhysicsHost, RigidBody } from '@ferret/physics';
import { friction } from '@ferret/physics-friction';

const host = world.addService(new PhysicsHost({ capabilities: [friction()] }));
host.materials.add('floor', { friction: 0.6, restitution: 0 }).add('puck', { friction: 0.6, restitution: 0 });
host.kernel.addBody({ shape: { type: 'plane', normal: [0, 1, 0], offset: 0 }, material: 'floor' });
host.enable('friction');

const puck = (name: string, x: number) => {
  const e = world.spawn(name, { position: new BABYLON.Vector3(x, 0.2, 0) });
  e.add('RigidBody', { shape: 'sphere', radius: 0.2, mass: 1, material: 'puck', velocity: [4, 0, 0], lockRotation: true });
  return e;
};
const normal = puck('normal', 0), greased = puck('greased', 20);
host.config('friction').set('entity', 'scale', 0.05, greased.id);
world.run(0.5);
```

`lockRotation: true` makes the sphere slide like a block (a puck, a crate) instead of rolling.

## Recipes

### An icy room

A zone (skill `ferret`) with a low `scale` makes everything in it slippery and nothing outside:

```ts
host.config('friction').set('zone', 'scale', 0.05, 'ice-rink');
```

The same from a level: `"friction": { "scale": 0.05 }` is level-wide; per-zone values go through the same call (or an agent's `physics.configure`).

### A skate, a ski, a rail

Anisotropy gives different grip along and across a direction. `axis` is the *along* direction in the world; across it the coefficient is `across` times larger. A skate on ice: a tiny `friction` on the blade material and `across ≈ 20–40` so it slides forward and holds sideways:

```ts
import { PhysicsHost, RigidBody } from '@ferret/physics';
import { friction } from '@ferret/physics-friction';

const host = world.addService(new PhysicsHost({ capabilities: [friction()] }));
host.materials.add('ice', { friction: 0.6, restitution: 0 }).add('blade', { friction: 0.02, restitution: 0 });
host.kernel.addBody({ shape: { type: 'plane', normal: [0, 1, 0], offset: 0 }, material: 'ice' });
host.enable('friction');
host.config('friction').set('world', 'anisotropy', { axis: [1, 0, 0], across: 40 });
const skate = world.spawn('skate', { position: new BABYLON.Vector3(0, 0.2, 0) });
skate.add('RigidBody', { shape: 'sphere', radius: 0.2, mass: 1, material: 'blade', velocity: [5, 0, 5], lockRotation: true });
world.run(0.5);
```

Anisotropy works on the **light kernel** (it needs contact hooks).

### Rolling resistance

A rolling ball slows at about `(5/7) · μr · g` (a solid sphere), where `μr` is the material's `rollingFriction`. `rollingScale` multiplies it: 0.5 means the ball rolls twice as far. Havok has no rolling resistance of its own; the capability adds it as a torque against the roll, limited so it can never reverse the spin within a step.

### Spinning in place

`pivot` is resistance to turning about the contact normal: a coin spinning on a table, a puck that should stop turning. Set it per scope or in the material (`pivotFriction`). Light kernel.

### A sticky or slick material instead

Prefer a **material** when it is a property of the stuff (rubber is grippy everywhere). Use `scale` when it is a property of *this place or this moment* (a spill, a rink).

## Parameters

| Value | Scopes | Unit | Safe range |
|---|---|---|---|
| `scale` | world, level, zone, entity | – | 0 – 5 |
| `rollingScale` | all | – | 0 – 5 |
| `pivot` | all | – | 0 – 1 |
| `anisotropy` | all | `{axis, across}` | `across` 1 – 100 |

## Kernel differences

| | Light kernel | Havok |
|---|---|---|
| `scale` | at every contact, before the solve | applied to each body's surface (its material's friction times the scale where it is) when the scale changes |
| `rollingScale` | native | rolling is a torque added by the capability |
| `pivot`, `anisotropy` | yes | **no** (need contact hooks) |

## Interplay

- Order: `friction` runs before `impact` in the same phase, so both change a contact before it is solved.
- Soil that deforms and sinks is the job of terrain (skill `ferret-physics-terrain`); use `friction` for the grip of rigid surfaces.

## Budget

One hook per contact on the light kernel. On Havok: one surface update per body *when its scale changes*, plus a torque per rolling body. Negligible next to the solve.

## Debugging

| Symptom | Cause |
|---|---|
| Nothing slides on ice | the capability is not enabled, or the scope key is wrong: the value is read at the **dynamic body's** entity and the contact point |
| A zone does nothing | no `ZoneService`, or the body's *position* is outside the zone |
| A ball never stops rolling | the material has no `rollingFriction`, and `rollingScale` multiplies zero |
| Anisotropy or pivot has no effect | you are on Havok |
| A block creeps on a slope | static friction below the tangent of the slope: compare `μ` with `tan(angle)` |

## Reference

`friction`, `FRICTION_DEFAULTS`, `scopedSurface`, `@ferret/physics-friction`.
