Slingshot
The rubber kicker above each flipper.
What it does
On a real table the ball presses the rubber, a switch closes, and a solenoid snaps a bar into it. The ball leaves faster than it came.
Slingshot builds the rubber as a child box, <id>_face, with a static Physics body, so the ball bounces off it like a wall. Each frame it checks every ball. When one is touching the middle of the face, it fires:
- The ball's speed across the face becomes at least
kick. Its run along the face is kept. - The face flashes
flashColor. - It says
slingshot.kickedandscore.add.
A worked kick, on a face that points along +X with kick 16:
The 3 into the face became 16. The 4 along it stayed.
The ends of the face are posts, not rubber. Only the middle activeSpan fires, 80% by default. After a kick the face waits cooldown seconds before it can fire again, so one touch is one kick.
The physics engine has usually bounced the ball before the system looks, so the ball may already be moving away. The kick does not care which way it was going.
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 Slingshot
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.
The face is one side of the triangle. Close the other two sides with a PinballWall so the ball cannot get behind it.
Props
from,to([x, z], defaults[0, 0]and[0, 2]). The two ends of the rubber.kick(number, default16). The least speed the ball leaves at, straight off the face.points(number, default10). Paid toownerEntityon each kick.ownerEntity(string, default"Player"). Who gets the points.cooldown(seconds, default0.15). Wait before the same face fires again.activeSpan(0 to 1, default0.8). How much of the middle of the face fires.ballRadius(number, default0.42). For the touch test.thickness(number, default0.5),height(number, default1). Size of the rubber.color([r, g, b], default[0.95, 0.95, 1]).flashColor([r, g, b], default[1, 0.85, 0.3]). Glow when it fires.
Events
Emits:
slingshot.kicked,{ entityId, ballId, points, speed }.speedis how fast the ball leaves.score.add,{ ownerEntity, points }.
Dependencies
MeshPrimitive, Physics and Shadow for the face. Score to count the points. The ball needs the ball tag and a dynamic Physics body.
The kick maths
- The kick is in
Slingshot.core.ts:slingKickfor the new velocity andonFacefor the touch test. - Each slingshot fires at most one ball per frame.









































