Wormhole
Two mouths that are one place: a body that flies into one comes out of the other.
What it does
Give two sphere entities a Wormhole with the same link and they are the two ends of one wormhole. A GravityBody whose centre comes inside either mouth comes out of the other at the same spot relative to it, with the same velocity, so its path carries on as if the two mouths were one hole in space. Either end leads to the other.
That drops the body inside the far mouth, which would send it straight back. A latch stops it: the mouth a body came out of stays closed to that body until it has left. The latch belongs to the body and lasts across frames, so a slow rock that sits in the exit for a while is never bounced home.
The jump is not made here. GravityBody makes it after every 1/120 s step, with throughWormholes from Wormhole.core, at the same point in the step where the aim line makes it for its ghost. A line that runs into one mouth comes out of the other exactly where the rock will. Checked once a frame instead, a rock lingered a step inside a mouth, and a black hole parked beside the mouth swallowed rocks the line showed passing through.
WormholeSystem is the mouths themselves. It pairs them, scales each sphere to the throat (0.42 of the catch radius), and flashes both ends when gravity.body.jumped names them. A mouth with no partner is drawn dim and slowed.
Setup
The engine work ships with the component, in adapter/register.ts. A project made with bjs create installs it for you: src/main.ts calls every adapter/register.ts that bjs download puts in the project, before the scene loads. If you boot the game from your own code, call it once, right after building the adapter:
Without it the mouths are bare spheres, the jumps work the same, and the System warns once: [Wormhole] no adapter plugin registered. The plugin is Babylon only. On Three or babylon-lite the call registers nothing.
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 Wormhole
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 rock flies along z = −2 into A and out of B, moved by B − A = (29, 0, −4), then carries on along z = −6 into the plant. The positions are SpaceGolf's First Jump level.
Props
link(string, default'') — mouths that share a link are the two ends of one wormhole. It takes exactly two: a link held by one mouth, or by three, opens nothing. An empty link never pairs.radius(number, default1.6) — how close a body's centre must come to the mouth's centre to be taken, in world units.color([r, g, b], default[0.45, 0.75, 1]) — the swirl's colour. Give both ends of a pair the same one, so a player can tell which mouth leads where.spin(number, default0.3) — swirl turns per second.
Events
gravity.body.jumped(listens) — flashes the two mouths it names asfromIdandtoId.
Wormhole emits nothing. The jump is announced by GravityBody.
Dependencies
MeshPrimitive— the throat sphere, and where the mouth is.
GravityBody does the moving and depends on Wormhole, not the other way round. Mouths in a scene with no GravityBody move nothing.
Notes
- A mouth takes a body by its centre. Unlike a mass surface, the body's own
radiusdoes not count. - Without the plugin only the throat is drawn, at 0.42 of the catch radius, so a rock is taken before it touches anything you can see.
- A parked partner closes the pair until it is handed out again. Give each pair its own link: SpaceGolf uses
spacegolf-<level>-<index>, so a pair never meets a mouth from another level. - MotionTrail also listens to
gravity.body.jumpedand restarts the rock's trail at the far mouth, so no streak crosses the field.











































