PinballWall
Walls from a list of points.
What it does
A pinball table is mostly walls, and almost none of them are square to the table. Guide rails lean in toward the flippers. The top is a round arch. Typing a centre, a length and an angle for every box is how tables end up with gaps.
PinballWall takes the path instead. Give it points, or an arc, and on its first frame it lays one box along each piece. Each box is a child entity named <id>_0, <id>_1 and so on, with a MeshPrimitive to see, a static Physics body to bounce off and a Shadow.
Each box is one wall thickness longer than the gap it covers. Cut exactly to length, two boxes at a bend leave a notch on the outside corner, and the ball finds it. With the extra length they overlap. On a 0.4 wall each box is 0.4 longer than its gap, so at a square corner each one runs half that past the joint.
Remove the wall entity and its boxes go with it.
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 PinballWall
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.
Points are [x, z] on the playfield. Arc angles are degrees: 0° points along +X and 90° points up the table (-Z), so an arch over the top runs from 180 to 0.
Props
points([x, z][], default[]). The path, point to point. Ignored whenarcis set.arc({ center, radius, from, to, segments? }, default none). Draw along part of a circle instead.segmentsdefaults to 8; more is rounder.closed(boolean, defaultfalse). Join the last point back to the first.height(number, default1.6). Wall height.thickness(number, default0.4). Wall thickness, and how much each box overlaps the next.y(number, default0). Height of the wall's base.color([r, g, b], default[0.32, 0.18, 0.5]).restitution(number, default0.5). Bounce, 0 to 1.friction(number, default0.05).castShadow(boolean, defaulttrue).
Events
None. It builds boxes and says nothing.
Dependencies
MeshPrimitive, Physics and Shadow. Each box carries one of each, so those systems must be in the scene.
Rebuilding a wall
- The boxes are built once. Changing
pointson a running wall does nothing; remove it and add a new one. - The geometry is in
PinballWall.core.ts:arcPointsturns an arc into points, andwallPiecesturns points into boxes.











































