Skip to main content

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

PropTypeDefaultDescription
soundIdsymbolSound ID returned by loadSound(). Required.
loopbooleanfalseWhether the sound loops continuously
volumenumber1Playback volume (0–1)
playbackRatenumber1Playback speed. 2 = double speed, 0.5 = half speed
persistUntilEndbooleanfalseWhen true, defers node destruction until playback finishes — useful for one-shot SFX nodes

Methods

MethodDescription
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

PropertyAccessDescription
isPlayinggettrue if the sound is currently playing
volumeget / setCurrent volume (0–1)
playbackRateget / setCurrent playback speed

Events

EventCallback signatureDescription
ended() => voidFires when playback reaches the end (non-looping only)
error(err: Error) => voidFires 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).

Build docs developers (and LLMs) love