Skip to content

How pallet load stability is calculated

The stability model in full: support measured against real contact surfaces, a centre-of-mass score, the 0.6/0.4 weighting, and what it cannot see.

Most pallet software reports a stability percentage and does not say what it measured. This page is the whole model, in the order the code runs it, with the constants imported from the file that uses them. Two numbers go in, one comes out:

stability = 0.6 × mean(support ratio) + 0.4 × centre-of-mass score
palletizer_full/optimizer.py::_stability, and its line-for-line port in web/lib/palletizer/optimizer.ts.

Support ratio: measured against what a case rests on

For each case, the support ratio is the fraction of its footprint that sits on a surface whose top is level with the case’s base, within a contact tolerance of 2 mm. A case on the deck scores 1.00. A case resting on two boxes scores the sum of the two overlap areas divided by its own footprint, capped at 1.00.

The distinction that matters: layer indices are never consulted. An earlier version of this engine credited a case with full support whenever its footprint overlapped anything in the layer below — so a case suspended over the gap left by a shorter neighbour scored 1.00 and the plan looked perfect. Measuring against real contact surfaces is what makes the number worth reporting, and it is why an upgrade changed some scores downward.

Centre-of-mass score: how far the mass has wandered

The mass-weighted centroid of the placed cases is compared with the centre of the deck, and the offset is normalised by half the deck’s diagonal. The validator refuses a plan whose normalised offset exceeds 0.25. A tall stack whose mass has drifted toward one corner is the failure mode that survives a static check and does not survive a forklift.

The score is not the verdict

A stability number cannot tell you that two cases occupy the same space. The validator is a separate pass, and these are its defaults:

min_support_ratio        0.8      per case, or the plan is refused
critical_support_ratio   0.5      below this, the violation is CRITICAL
max_com_offset_norm      0.25     normalised centre-of-mass offset
max_overhang_mm          0      nothing may hang off the deck
max_interpenetration_mm3 1000   numerical tolerance, not a budget
require_sku_conservation true    every case ordered is placed or listed unplaced
enforce_crush            true    when per-SKU limits are supplied
DEFAULT_VALIDATION_POLICY, web/lib/palletizer/validation.ts. The CLI's --min-support overrides the first.

It also checks bounds, interpenetration, floating cases, the height and mass budgets, and that the stack can be built bottom-up — a plan that is stable once finished but impossible to assemble in order is a plan nobody can build.

Read checks_skipped, every time

Crush strength is the check that most often does not run, because it needs a per-SKU limit in kilograms and most orders do not carry one. When it does not run it is named in checks_skipped rather than quietly passed. A plan reported as valid without naming its skipped checks is the easiest way to mislead somebody with this software, which is why every surface — CLI, API, MCP server — returns that list unconditionally.

What the model does not see

Friction coefficients, case deformation under load, stretch wrap, transport dynamics, deck condition, and whether the cases in your warehouse match the dimensions in your export. The model is geometry and mass. It is a strong filter for arrangements that are obviously wrong and it is not a certificate that a load will travel — nothing in this project is certified, and no software can certify itself.

Next: how the packer chooses an arrangement in the first place.

Upload your own SKU CSV and get a validated, sealed plan in the browser.

The engine is Apache-2.0 and unmetered on your own machines — what the hosted plans add is a signature only this service can make, state it keeps for you, and compute on its hardware.

Every figure on this page is imported from the code that uses it, so it cannot drift from the engine. Run the engine on your own SKUs or check a plan you already have. The limits are written down in known risks.