Audio
Awaken plays sound through a single lightweight WebAudio backend: attach an AudioSource to an object to play a clip when the game starts, or trigger sounds on demand from a script with api.playSound.
The model
Audio in Awaken is deliberately small. There are two ways sound reaches your player's speakers:
- AudioSource component - a "play this clip when the scene starts" flag you attach to an object in the editor. Good for ambience, music, or a one-shot that fires on spawn.
api.playSound(clip, opts?)- a script call that plays a clip at any moment (a jump, a pickup, a hit). This is how most gameplay audio works.
Both funnel into the same AudioEngine interface, a tiny pluggable backend whose only method is play(clip, opts?). The editor's Game view and the shipped player each provide a browser WebAudioEngine (WebAudio API); headless contexts and tests use a NoopAudioEngine that does nothing. Because the runtime only ever talks to the AudioEngine interface, gameplay logic stays fully testable without a browser.
The AudioSource component
Add AudioSource from the Inspector's + Add Component menu. It has four fields:
| Field | Type | Default | Meaning |
|---|---|---|---|
clip | asset ref | "beep" | Which audio clip to play (an imported clip id, or the built-in beep). |
volume | number | 1 | Linear gain, 0–1. |
loop | bool | false | Repeat the clip until the scene ends. |
playOnStart | bool | true | Play automatically the moment Play begins. |
When you enter Play, the runtime walks every object that has both a Transform and an enabled AudioSource, and for each one whose playOnStart is set it calls play(clip, { volume, loop }). A disabled component (its enabled checkbox off) is skipped. That is the entire lifecycle - an AudioSource fires once at start; there is no built-in "play again" trigger on the component itself. For repeated or conditional sounds, drive audio from a script instead.
📸 Screenshot - save as img/media-audiosource-inspector.png
The Inspector showing an AudioSource component with its clip, volume, loop, and playOnStart fields on a selected object.
Where clips come from
The engine ships with one built-in sound, the beep - a short 440 Hz tone the WebAudioEngine synthesises on the fly. It is also the fallback for any clip id: name a clip the engine has not loaded and it plays the beep rather than failing silently. That is why AudioSource.clip defaults to "beep".
No audio import yet
There is currently no audio-import pipeline. No loader decodes .wav / .mp3 / .ogg files into the engine, and nothing calls the engine's addClip(id, buffer) method - so the beep is, in practice, the only sound Awaken can play today. Whatever string you put in an AudioSource.clip field or pass to playSound, it resolves to the beep. The editor's WebAudioEngine already contains a buffer-playback path (an addClip method backed by a decoded-AudioBuffer map) that is ready for the day clip import lands; the shipped player's engine is beep-only by construction.
Triggering sounds from scripts
Inside a script, call api.playSound:
ts
export default class Coin implements ScriptBehavior {
onStart(api: ScriptApi) {
// Play the built-in beep at half volume when this coin spawns.
api.playSound("beep", { volume: 0.5 });
}
onUpdate(api: ScriptApi) {
if (api.input.isDown(" ")) { // spacebar is the " " key
api.playSound("jump"); // any id resolves to the beep until audio import lands
}
}
}The signature is playSound(clip: string, opts?: { volume?: number; loop?: boolean }). Both options are optional - omit them for a one-shot at full volume. See the full surface on the Scripting API page.
Playback is fire-and-forget: playSound returns nothing, so there is no handle to stop an individual sound later. A looping sound started this way plays until the scene changes or the game ends.
How the WebAudio engine works
The WebAudioEngine (apps/editor/src/audio/webAudio.ts) is a thin wrapper over the browser's WebAudio API:
- It lazily creates a single
AudioContext. Because browsers block audio until a user gesture, the engine listens for the firstpointerdownorkeydownand resumes the context then. Sounds that "fire" before the player has clicked or pressed a key stay silent until that first interaction. addClip(name, buffer)stores decodedAudioBuffers in a map keyed by clip id.play(clip, opts)looks the clip up. On a hit it wires aBufferSource → GainNode → destinationgraph, setsloopandgain.valuefrom the options, and starts it. On a miss it plays the beep.
Because nothing currently calls addClip, the buffer map is always empty, so the "clip loaded" branch below is dormant and every play takes the beep path today. The shipped player uses a simpler WebAudioEngine with no buffer map at all - it always synthesises the beep.
Limitations (be honest)
Awaken audio is intentionally minimal for now:
- No imported audio. There is no audio-import pipeline yet, and the shipped player's engine is beep-only by construction - so the built-in 440 Hz beep is currently the only sound you can actually hear. Any other
clipid falls back to it. - 2D only. There is no positional / 3D audio - no distance falloff, no panning by object position.
volumeis a flat gain applied to every listener. - No stop / pause API. A started sound plays to its end (or forever, if looping). Looping sources stop only when the scene changes or the game ends.
- No mixing groups or effects. Each
playbuilds an independent source → gain → destination chain.
If you need one of these, script your gameplay around the fire-and-forget model (for example, keep looping ambience on an object you destroy to "stop" it).
See also
- Scripting API - the full
ScriptApi, includingplaySound. - Scripting Overview - how scripts attach to objects and run.
- Import Overview - how audio clips get into a project.
- In-Game UI - the other overlay system that ships with your game.