logo
Unlock

Install with the CLI:

bjs download RoadAudio

RoadAudio

The sound of a road war, made on the fly with Web Audio. No files to load.

What you hear

  • the engine, with a pitch that climbs through three gears as you speed up, and a brighter tone under throttle;
  • gun cracks, Gunner shotgun booms, missile whooshes, and the splat of oil and the puff of smoke;
  • explosions: a noise rumble that dies away over a low thump;
  • metal on metal for rams and crashes, and a ping off armour;
  • a tyre squeal while your car slides;
  • the bomber's rotor chop while one is up;
  • a police siren while a patrol car chases you, louder as it gets close;
  • a chime in the weapons truck.

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:

  1. 1Scaffold a project
    npm create @babylonjsmarket/arcade@latest my-game

    World, renderer, and dev server — ready to run.

  2. 2Install dependencies
    cd my-game && npm install
  3. 3Add RoadAudio
    bjs download RoadAudio

    Copies its source into src/ so the scene resolves.

    First time? Run bjs login once.

  4. 4Paste the scene into src/scenes/arcade-room.ts and run
    npm run dev

    SceneLoader builds the world from the JSON; reload to rebuild.

JSON
{
  "Game": {
    "components": {
      "RoadAudio": { "player": "Player", "volume": 0.7, "reach": 140 }
    }
  }
}

Starting the sound

Browsers only let a page start audio during a user gesture. The audio is set up on the first key press, click or tap, inside that gesture. Keys reach it as keyboard.keydown events, so the scene needs KeyboardInput; a click or a lifted finger is caught on the page itself, so a HUD's START button and a phone start the sound too.

M mutes and unmutes. While the world is paused, all sound fades out.

Engine in three gears

TypeScript
export function engineVoice(speed: number, throttle: number, topSpeed: number): EngineVoice {
  const f = Math.max(0, Math.min(1, Math.abs(speed) / Math.max(1, topSpeed)));
  const gears = 3;
  const g = Math.min(gears - 1, Math.floor(f * gears));
  const inGear = f * gears - g; // 0..1 within the gear
  const freq = 42 + g * 9 + inGear * 34;
  return {
    freq,
    cutoff: 380 + throttle * 900 + f * 700,
    gain: 0.045 + throttle * 0.035 + f * 0.02,
  };
}

Each gear is a third of topSpeed. Inside a gear the note climbs 34 Hz. At the next gear it drops back, but each gear starts 9 Hz higher than the last. With the default top speed of 52 m/s:

GearSpeed (m/s)Note (Hz)
10 to 1742 up to 76
217 to 3551 up to 85
335 to 5260 up to 94

The System plays that note on two saw waves, one tuned a hair sharp so the pair beats like an engine, plus a sine an octave below. They pass through a low-pass filter set to cutoff, which opens under throttle.

Sounds from noise

Every crack, boom and squeal starts from one two-second buffer of white noise, made once when the audio starts:

TypeScript
export function noiseSamples(length: number, seed = 1): Float32Array {
  const out = new Float32Array(length);
  let s = seed >>> 0 || 1;
  for (let i = 0; i < length; i++) {
    s = (Math.imul(s, 1664525) + 1013904223) >>> 0;
    out[i] = (s / 4294967296) * 2 - 1;
  }
  return out;
}

A filter keeps the part of the hiss that sounds like the thing, and a quick fade gives it a shape. Each burst starts at a random spot in the buffer, so no two shots sound the same. Gun cracks are rationed to about 22 a second, so a fast gun can't pile up hundreds of sounds.

SoundMade from
gun cracknoise through a band-pass at 2600 Hz, 0.05 s long
ramnoise at 1400 Hz, louder the harder the hit
explosionnoise through a low-pass sliding from 1800 Hz down to 120 Hz, over a sine thump falling from 110 Hz to 38 Hz
missilenoise through a band-pass sweeping up from 400 Hz to 3200 Hz
tyre squeala narrow band of noise at 2400 Hz
rotornoise at 220 Hz, cut on and off by a square wave ROTOR_HZ (11) times a second
sirena triangle tone swept around SIREN.hz (1000 Hz) by 360 Hz, 0.3 times a second
truck chimethree square-wave notes on docking, two on a refit

Fading with distance

TypeScript
export function falloff(distance: number, reach: number): number {
  if (reach <= 0) return 1;
  const k = 1 - Math.min(1, Math.max(0, distance) / reach);
  return k * k;
}

Halfway to reach, a sound plays at a quarter of its volume. Past reach it is skipped. The siren follows the nearest car chasing you. The missile whoosh and the truck chime play at full volume wherever they happen.

Settings

FieldDefaultWhat it does
player"Player"The car whose engine you hear. Distances are measured from it.
volume0.7Master volume, 0 to 1.
muteKey"KeyM"KeyboardEvent.code that mutes and unmutes.
reach140Metres past which a sound is not heard. 0 turns fading off.
topSpeed52Speed in m/s at the top of third gear. Match it to CarDrive's maxSpeed.
mutedfalseLive mute state. Not read from the scene; the mute key and roadAudio.mute flip it.

Events

EventPayloadDirection
keyboard.keydown{ code }listens: starts the audio, and the mute key
roadAudio.mute{ muted? }, leave out muted to togglelistens
autoRunnerWeapons.fired{ entityId, weapon, x, z }listens
autoRunnerDriver.fired{ entityId, x, z, targetId }listens
autoRunnerProjectile.exploded{ entityId, owner, kind, x, z, radius, ground }listens
autoRunnerProjectile.ricochet{ entityId, targetId, owner, kind, x, z }listens
health.died{ entityId }listens
carCollision.hit{ a, b, x, z, closing, dvA, dvB }listens, above 5 m/s closing
carCollision.crashed{ entityId, kind, x, z, closing }listens, above 4 m/s closing
carCollision.spinout.started{ entityId }listens, player only
carCollision.spinout.ended{ entityId }listens, player only
autoRunnerBomber.arrived{ entityId }listens
autoRunnerBomber.left{ entityId }listens
autoRunnerDriver.pursuit{ entityId, targetId, on }listens
supplyTruck.docked{ entityId, truckId, offers, seconds }listens
supplyTruck.upgraded{ entityId, offer }listens

RoadAudio emits nothing.

Needs

  • MeshPrimitive: where the player, each wreck and each police car is, for the distance fade.
  • CarDrive: the player's speed and throttle, for the engine note.
  • CarCollision: rams, crashes and spin-outs, and the ghost tag that silences a lost car's engine.

More like this

AutoRunnerBomber
AutoRunnerDriver
AutoRunnerProjectile
AutoRunnerWeapons
CarCollision
CarDrive
Health
KeyboardInput
MeshPrimitive
SupplyTruck
AiDriver
AutoRunnerDirector
AutoRunnerHUD
BilliardHUD
BoatMode
BoatWater
CarPaint
CardFan
ConicPath
ContactDamage
ContactImpulse
DayNightCycle
Headlights
HealthBar
HighwayRoad
HighwaySpawner
LiveTuner
PinballHUD
PokerHUD
PokerTableCards
RoadCamera
RoadFX
RoadHazard
RoadSpawner
RoadTreadmill
RoadsideScenery
Score
Scoreboard
SmokeScreen
SpaceGolfHUD
SpaceShooterHUD
SpaceShooterScore
SurfacePatch
TrafficConvoy
TrajectoryPreview
Waypoint
WeaponsVan

Was this page helpful?

We read every note — tell us what's working and what isn't.

↑↓ NavigateEnter SelectEsc CloseCtrl+K Open Search