Documentation Index
Fetch the complete documentation index at: https://mintlify.com/sanchedev/fraxel/llms.txt
Use this file to discover all available pages before exploring further.
Fraxel’s audio system is built on top of the browser’s Web Audio API. Sounds are loaded asynchronously and decoded into AudioBuffer objects identified by opaque symbol IDs. The <audio-player> node manages an AudioBufferSourceNode and GainNode for each sound, giving you play/pause/stop control along with real-time volume and playback-rate adjustment. Because browsers require a user gesture before audio can start, the system exposes getAudioContext() so you can resume the context at the right moment.
Loading Sounds
Use loadSound() to fetch and decode an audio file before playing it. The function returns a Promise<symbol> — a stable identifier you store and pass to <audio-player soundId={...}>.
import { loadSound } from 'fraxel/assets'
const shootSound = await loadSound('/assets/sounds/shoot.mp3')
const bgMusic = await loadSound('/assets/sounds/music.mp3')
Calling loadSound() with the same URL more than once returns the same cached symbol without re-fetching or re-decoding the file. Module-level await at the top of a scene file is the recommended pattern — sounds are ready before the scene mounts.
AudioPlayer Node
The <audio-player> JSX node wraps the Web Audio playback logic. Place it as a child of any node in your scene tree.
import { useNode, useEvent } from 'fraxel/hooks'
import { PrimaryNode } from 'fraxel'
import { loadSound } from 'fraxel/assets'
const SHOOT = await loadSound('/assets/shoot.mp3')
function Gun() {
const audio = useNode(PrimaryNode.AudioPlayer)
const clickable = useNode(PrimaryNode.Clickable)
useEvent(clickable, 'clicked', () => {
audio.node.play()
})
return (
<sprite ref={clickable} textureId={GUN_TEX}>
<audio-player ref={audio} soundId={SHOOT} volume={0.8} />
</sprite>
)
}
Props
| Prop | Type | Default | Description |
|---|
soundId | symbol | — | Sound ID returned by loadSound(). Required. |
loop | boolean | false | Whether the sound loops continuously |
volume | number | 1 | Playback volume (0–1) |
playbackRate | number | 1 | Playback speed. 2 = double speed, 0.5 = half speed |
persistUntilEnd | boolean | false | When true, defers node destruction until playback finishes — useful for one-shot SFX nodes |
Methods
| Method | Description |
|---|
play(offset?) | Starts or resumes playback. Optional offset sets the start time (s) |
pause() | Pauses without resetting position |
stop() | Stops playback and resets position to 0 |
Getters / Setters
| Property | Access | Description |
|---|
isPlaying | get | true if the sound is currently playing |
volume | get / set | Current volume (0–1) |
playbackRate | get / set | Current playback speed |
Events
| Event | Callback signature | Description |
|---|
ended | () => void | Fires when playback reaches the end (non-looping only) |
error | (err: Error) => void | Fires if playback fails (e.g. context suspended) |
AudioContext
The AudioContext is created lazily the first time any audio is played. Browsers block audio playback until the user has interacted with the page — if you try to play before a gesture, the context will be in "suspended" state and no sound will be heard.
import { getAudioContext } from 'fraxel/audio'
// Check and resume after a user gesture (click, key press, etc.)
function onFirstClick() {
const ctx = getAudioContext()
if (ctx.state === 'suspended') {
ctx.resume()
}
}
Browser autoplay restrictions — All major browsers block audio until the user interacts with the page. Always call getAudioContext().resume() inside a user-initiated event handler (click, key press, pointer down) before triggering playback. If you skip this step, sounds silently fail to play.
Complete Example
The example below sets up a looping background music track and a one-shot shoot SFX that plays on a button click:
import { useNode, useEvent, useMount } from 'fraxel/hooks'
import { PrimaryNode } from 'fraxel'
import { loadSound } from 'fraxel/assets'
import { getAudioContext } from 'fraxel/audio'
const BG_MUSIC = await loadSound('/assets/music.mp3')
const SHOOT = await loadSound('/assets/shoot.mp3')
function GameAudio() {
const music = useNode(PrimaryNode.AudioPlayer)
const sfx = useNode(PrimaryNode.AudioPlayer)
useMount(() => {
// Resume the AudioContext on first mount — assumes a user gesture has occurred
const ctx = getAudioContext()
if (ctx.state === 'suspended') ctx.resume()
music.node.play()
})
return (
<transform>
{/* Background music — loops at 30% volume */}
<audio-player ref={music} soundId={BG_MUSIC} loop volume={0.3} />
{/* One-shot SFX — full volume */}
<audio-player ref={sfx} soundId={SHOOT} />
</transform>
)
}
For the assets API and texture loading alongside sound loading, see API: Assets. For node lifecycle hooks used here, see API: Hooks (Core).