Physics · function

fingerprintColliders

A stable hash of the static geometry a simulation ran against.

Explained in Your first game, Rigid bodies.

function fingerprintColliders(set: ColliderSet): string
import { fingerprintColliders } from '@driftengine/physics';

Parameters

ParameterTypeDescription
setColliderSet

In depth

A recording made of a deterministic simulation is only replayable against the world it was recorded in. When world generation changes — a fixed bug, a retuned curve, a new build — recordings from before it desync, and they do it silently: the run is reproduced faithfully against geometry that is no longer there, so what appears on screen is a body moving through empty space with nothing to explain it.

Storing this alongside a recording lets a caller compare the world it has against the world the recording was made in, and refuse rather than guess.

Determinism is the whole contract. The same colliders must give the same string on every machine and in every build, so this reads the raw bytes of the bounds array: a float's decimal rendering is a platform question and its bit pattern is not. Nothing here iterates a Map or a Set, both of which would make the answer depend on insertion order.

No tolerance, deliberately. A sub-millimetre difference changes the answer. Whether a given difference matters is the caller's judgement, and a hash that quietly rounded would take that judgement away from every caller at once.

FNV-1a over 64 bits, carried as two 32-bit halves because JavaScript's bitwise operators are 32-bit and a single accumulator would silently lose the top half. Not a cryptographic hash: this detects change, it does not resist forgery, and a caller that needs the second property needs crypto.subtle and an await.

On a set that has been mutated, this answers "the same set with the same history" and not "the same world". A set can take and drop groups now, and a slot freed by one region is refilled by another, so the same geometry sits at different indices depending on the path driven to reach it.

Replay is not what that costs. The same inputs drive the same path, which produces the same load order and so the same slots, and a recording replays against the set it was recorded against exactly as it always did. What it costs is comparing two sessions that arrived at the same world by different routes.

fingerprintBodies's escape hatch does not transfer, which is worth saying because the two functions are otherwise the same construction. There, a caller wanting the multiset sorts what it feeds in, because it hands over an array. Here a caller hands over an object whose slot order is a load history they neither chose nor can observe, so there is nothing for them to sort. A canonical variant hashing live slots sorted by their packed bytes would answer the cross-session question and is about fifteen lines; it is not here because nothing is asking it yet.