Making a game

Sound

Sound effects, loops and music that changes with the game.

This covers a sound effect a rule raises, the volume and mute, loops, music and a cutscene's sound. Sound files play through audio, notes through synthaudio, a whole song of devices, tracks and clips through audiodaw, and the volume is kept in the store.

A sound a rule raises

  1. Put the file in games/<game>/assets/ and add a sound entry for it to the scene's loader catalogue, world.loader.assets.
  2. Write the cue table as world.audio in the scene file, or once in the manager's <game>.scene.json when every scene uses it.
  3. Add store and audio to the scene's plugins, the audio prefab, the volume's starting values in the store prefab, and one { "is": "audio" } entry in actors ahead of every actor that raises a sound in its before.
  4. Put the cue in the kind's definition in assets/<game>.json, as a sounds table of event to cue.
  5. Raise it in the rule that made the change, with sound(w, cue, at, from). A sound of the seat's own thing is raised from its player row and is heard on the frame after the key; any other goes on the ledger.
  6. Bind the key that sends the act in player.client.ts, and give the mute key a function that flips the store's muted key on the page.
"world": {
  "loader": { "assets": { "knock1": { "type": "sound", "url": "/games/&lt;game&gt;/assets/knock.wav" } } },
  "audio": {
    "hold": 0.25,
    "cues": { "knock": { "sounds": ["knock1"], "gain": 0.8 } },
    "volume": { "level": "volume", "muted": "muted" }
  },
  "store": { "volume": 0.8, "muted": false }
},
"prefabs": { "audio": { "step": 0, "next": 1, "live": [] } }

The definition then holds "sounds": { "bump": "knock" }, and the rule calls sound(w, def(w, me.kind).sounds.bump, undefined, me.id).

Where each piece goes

PieceHome
The sound fileA sound entry of world.loader.assets
The cue tableworld.audio
Which cue a kind playsThe kind's definition, under sounds
Volume and muteStore keys, named by world.audio.volume, starting values in the store prefab
The song playing nowA field of a row, such as the cutscene row's music

Placed sound

Under render3d's camera a placed sound needs world.audio.space, in metres. Under render2d with a world.render2d it needs world.audio.screen, in canvas pixels. With neither listener every sound plays unplaced.

Loops and music

  1. Name each loop in world.audio.loops: one whose gain follows a field of a row, one that follows the camera's height, or a named loop that plays the sound a field names and crossfades over fade seconds.
  2. Give each song's catalogue entry "preload": true, so it starts when the field names it.
  3. For music as notes, list the project files in world.synthaudio.projects and the field that picks one in world.synthaudio.music. Name the same store keys in world.synthaudio.volume as in world.audio.volume so one mute covers both, and list store before synthaudio.
  4. For a whole song, a project of devices, tracks and clips with every voice made when it loads, use audiodaw. Give the song a kind of the game's own whose look loads the project and plays it, list audiodaw in the scene's plugins, and set Tone's buffer in world.audiodaw.

A cutscene's sound

Set world.cutscene.mods to { "play": "audio" }, list audio, and stand the ledger's entry ahead of the cutscene row. The cutscene's sound and say cues then name cues of world.audio.cues, and its music cue sets the row's music field for a named loop to play.

Checking it

  • Run the kind's test, yarn vitest run games/<game>/<kind>.sim.test.ts: after the press the entry is on the player row's voiced list, or on the ledger for any other row, with the definition's cue.
  • Run the page test beside player.client.ts, with an AudioContext stand-in: one sound starts once, through the cue's gain and the store's volume, and the mute key sets that volume to 0.
  • Play it from games/<game>/index.html with real key presses, which unlock the browser's audio as a person's would; a script's resume() before any input leaves it locked. Then run the game's server and open client.html in two tabs: the seat's own sound is heard on the key, and the other tab hears it after.