RoadFX
Blasts, sparks, smoke and dust, all set off by things that already happen in the game.
What it does
RoadFX decides nothing. It listens to the game and draws what each event looks like:
| When | You see |
|---|---|
| a car dies | smoke, fireball, flash and debris, a scorch on the road, and a fire on the wreck until it is cleared |
| a missile or bomb bursts | the same blast, sized to the burst radius |
| a bullet or shell hits, or bounces off armour | sparks |
| a missile is fired | a smoke trail that follows it |
| two cars hit | sparks, and a camera shake if the player was in it |
| a car hits a tree, post or house | sparks and dust, and a shake if it was the player (a bridge rail gives sparks only) |
| a car spins out | tyre smoke and skid marks while it slides |
| a car runs onto the grass | dirt kicked up behind it |
| a car goes into the river | a splash |
| a smoke screen is laid | a drifting cloud |
| a wreck is cleared | a puff of smoke, and its fire goes out |
Anything tagged air, like a helicopter, blows up where it flies, with no scorch and no fire left behind.
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 RoadFX
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.
Put RoadFX on one entity, and call registerRoadFXExtension(adapter) once at bootstrap. Only Babylon has the drawing plugin. Without it the game plays the same, nothing burns, and RoadFX warns once in the console.
Effects as recipes
Every effect is a recipe: a short list of numbers in RoadFX.core. Here is the spark:
blend: 'add' makes it glow. Smoke and dust use 'standard', so they hide what is behind them. endSize is how big a bit ends up, as a share of its start size. A gravity of −9 pulls sparks down. Smoke has a small upward one, so it rises.
Bigger blasts
scaled(recipe, k) resizes a recipe for a bigger or smaller event:
Anything tagged shover or helicopter dies at k = 1.4. The fireball goes from 60 bits to 118 (60 × 1.4²). Each bit is 1.4 times as big but flies only 1.18 times as fast. Count grows with area, size with width, and speed only with the square root, so a big blast fills more sky without throwing sparks across the map. The count stops at three times the base.
A missile or bomb burst uses k = radius ÷ 5, and never less than 0.6. Then density multiplies the count, so 0.5 halves every effect.
Draw order
Babylon draws particle systems in the order they were made. A blast makes its smoke before the fireball, so the fire glows through the smoke instead of sitting behind it.
Babylon also throws away a particle system's texture when the system goes. Every effect here shares one sprite, so a spent effect is dropped without its texture. Otherwise the first flash to end would blank every other effect on screen.
Settings
| Field | Default | What it does |
|---|---|---|
density | 1 | Multiplies every particle count. Use 0.5 on a weak GPU. |
explosionShake | 0.7 | Camera shake (0 to 1) a blast sends. RoadCamera scales it down with distance from the car it follows. |
scorchSeconds | 18 | Seconds a scorch mark stays on the road. |
player | "Player" | Entity name of the player's car. A hit or crash on it shakes the camera. |
Events
| Event | Payload | Direction |
|---|---|---|
roadCamera.shake | { trauma, x?, z? } | emits |
health.died | { entityId } | listens |
autoRunnerProjectile.exploded | { entityId, owner, kind, x, z, radius, ground } | listens |
autoRunnerProjectile.hit | { entityId, targetId, owner, kind, x, z, damage } | listens |
autoRunnerProjectile.ricochet | { entityId, targetId, owner, kind, x, z } | listens |
autoRunnerProjectile.launched | { entityId, kind, owner, x, z } | listens |
autoRunnerProjectile.ended | { entityId, kind, x, z, reason } | listens |
carCollision.hit | { a, b, x, z, closing, dvA, dvB } | listens |
carCollision.crashed | { entityId, kind, x, z, closing } | listens |
carCollision.spinout.started | { entityId } | listens |
carCollision.spinout.ended | { entityId } | listens |
carCollision.surface.changed | { entityId, surface } | listens |
carCollision.drowned | { entityId, x, z } | listens |
roadHazard.laid | { entityId, kind, x, z, radius, life } | listens |
highwaySpawner.released | { entityId, pool, reason } | listens |
A hit only throws sparks when closing is 4 m/s or more. Only bullet and shell hits spark. Only kind: "missile" launches get a trail, and only kind: "smoke" hazards get a cloud.
Needs
- MeshPrimitive: where each car is, so blasts, wreck fires and tyre smoke start in the right place.
- CarDrive: each car's heading, so skid marks land under the rear wheels.





















































