Skip to main content
← All demos

Newton's Cradle

game.ts
const renderLayers = {
foreground: 1 << 0,
};

export const createNewtonsCradleGame = async (): Promise<Game> => {
const { game, world, renderContext, time } = createGame('demo-game');

createCamera(world, {
isStatic: true,
cullingMask: renderLayers.foreground,
verticalWorldUnits: DEMO_VERTICAL_WORLD_UNITS,
});

await createCradle(world, renderContext, renderLayers.foreground, {
x: 0,
y: DEMO_VERTICAL_WORLD_UNITS * 0.3,
});

const collisionPairs: CollisionPair[] = [];
const collisionManifolds: CollisionManifold[] = [];
const contactConstraints: ContactConstraint[] = [];

// The revolute joint solver must run after collision resolution so each
// ball's hinge gets the "last word" on velocity each tick;
// `createArmEcsSystem` only needs to run before `createRenderEcsSystem`,
// so its updated arms are reflected in this tick's render.
world.addSystem(createGravityEcsSystem(time));
world.addSystem(createBroadPhaseEcsSystem(collisionPairs));
world.addSystem(
createNarrowPhaseEcsSystem(collisionPairs, collisionManifolds),
);
world.addSystem(
createCollisionResolutionEcsSystem(
collisionManifolds,
contactConstraints,
time,
),
);
world.addSystem(createRevoluteJointEcsSystem(time));
world.addSystem(createArmEcsSystem());
world.addSystem(createCameraEcsSystem(time));
world.addSystem(createRenderEcsSystem(renderContext));
world.addSystem(createEulerIntegrationEcsSystem(time));

return game;
};

Five balls are each hinged to their own pivot on a shared frame by a RevoluteJoint, spaced so neighboring balls just touch at rest, exactly like the desk toy. The joints only keep each ball swinging about its own pivot, they play no part in transferring momentum between balls, that comes entirely from the engine's ordinary collision resolution between the balls themselves. The leftmost ball starts pulled back and is released exactly once, when the scene is built, swings in, and the impact carries down the row to pop the rightmost ball out. From there it's left alone, so it settles the same way a real cradle does after a single push.