BallSearch
Shakes loose a ball that has stopped where no ball should.
What it does
Pinball machines have done this since the 1980s. If nothing has been hit for a while, the game fires its kickers one at a time to free a ball wedged somewhere the designer did not foresee. Players call it a "ball search."
This watches every ball tagged ball. A ball is stuck when all of these are true for stillSeconds:
- it stays within
stillRadiusof one spot, - it is not in a rest zone (the plunger tip, a saucer),
- no hold key is down (a player cradling the ball on a raised flipper).
Then it nudges the ball up the table, since a ball only stops where something below holds it. The nudges go left, then right, each one grow times harder. If every nudge fails, it says ballSearch.gaveUp and sends giveUpEvent.
It is the second of two defences. PinballTable's trap finder (PinballTable.traps.ts) proves, from the table's geometry, that there is no pocket a ball can settle in. BallSearch covers what geometry cannot see: a ball balanced on an edge, or pinned by a moving part.
Use it in a scene
A scene is just data — a list of entities, each with its components. At startup the SceneLoader turns this JSON into a live world; edit the file and reload to rebuild it.
Build it from scratch with the bjs CLI:
- 1Scaffold a project
World, renderer, and dev server — ready to run.
- 2Install dependencies
- 3Add BallSearch
Copies its source into src/ so the scene resolves.
First time? Run bjs login once.
- 4Paste the scene into src/scenes/arcade-room.ts and run
SceneLoader builds the world from the JSON; reload to rebuild.
Props
ballTag(default"ball") — which balls to watch.stillRadius(default0.15) — a ball that stays this close to one spot is still.stillSeconds(default2.5) — how long it must be still before a search.restZones— places a ball may sit, each{ at: [x, z], radius }.holdKeys— key codes that pause the search while held.nudge(default8),grow(default1.5) — the first nudge's speed, and how much harder each next one is.attempts(default4),interval(default0.8s) — nudges per search, and the gap between them.giveUpEvent(default empty) — an event to send when every nudge fails, such asplunger.serve.
Events
- Emits
ballSearch.nudged—{ ballId, attempt, at: [x, z], speed }. - Emits
ballSearch.gaveUp—{ ballId, at: [x, z] }. - Listens for
keyboard.keydown/keyboard.keyup— to know which hold keys are down.








































