> ## Documentation Index
> Fetch the complete documentation index at: https://docs.eucaengine.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Audio

> Play a spatial sound source at a position, list it, and stop it — and understand why the headless host returns 'AudioEngine not initialized' until you run on a host with an audio device.

Euca's audio system (`euca-audio`) wraps [Kira](https://docs.rs/kira) for mixing: `/audio/play` loads a clip
and spawns an `AudioSource` entity (global or spatial), `/audio/list` reports active sources, and
`/audio/stop` silences one. The route logic — loading, spawning the source entity, tracking its state — is
headless, but **actual playback needs an `AudioEngine` bound to an OS audio device**, which a bare
[headless host](/quickstart) doesn't register. So this page shows the goal, the real error you'll see
without a device, and what a host with audio enables.

## Play and stop a spatial sound

Point `/audio/play` at a clip; add a `position` `[x,y,z]` to make it spatial, with `max_distance` for the
falloff radius:

```bash theme={null}
curl -s -X POST http://localhost:3917/audio/play \
  -H 'Content-Type: application/json' \
  -d '{"path":"assets/audio/explosion.wav","volume":0.8,"position":[3.0,0.0,1.0],"max_distance":50.0}'
# Headless host (no audio device): {"error":"AudioEngine not initialized","ok":false}
```

That `AudioEngine not initialized` is the **real response from a bare headless host** — the handler looks up
an `Arc<Mutex<AudioEngine>>` resource and bails before loading the clip, because no audio engine is
registered. (On a host that registers one but has no working audio hardware, `AudioEngine::new()` fails
earlier, at construction.) The malformed request is still validated first — a missing path short-circuits:

```bash theme={null}
curl -s -X POST http://localhost:3917/audio/play -H 'Content-Type: application/json' -d '{}'
# {"error":"Missing 'path' to audio file","ok":false}
```

On a server that registers an `AudioEngine` on a machine with an audio device, the same `play` call loads
the clip, spawns the source entity, and returns its id, clip handle, and spatial flag — e.g.
`{"ok":true,"entity_id":7,"clip":0,"spatial":true}`. That source then appears in `/audio/list`, and
`/audio/stop` silences it by entity id:

```bash theme={null}
curl -s http://localhost:3917/audio/list
# Headless host: {"count":0,"sources":[]}
# With audio:      {"count":1,"sources":[{"entity_id":7,"clip":0,"volume":0.8,"spatial":true,"playing":true,"looping":false}]}

curl -s -X POST http://localhost:3917/audio/stop -H 'Content-Type: application/json' -d '{"entity_id":7}'
# Stopping a source that isn't there: {"ok":false,"message":"Entity 7 not found"}
```

## Behavior and gotchas

The things you only find out by running it:

* **The headless host has no audio device, so `/audio/play` returns
  `{"error":"AudioEngine not initialized","ok":false}`.** Playback is a client-side concern — register an
  `AudioEngine` on a host with real audio hardware to enable it. `/audio/list` returns
  `{"count":0,"sources":[]}` on a bare headless host for the same reason.
* **A missing `path` is rejected before the engine check** with `{"error":"Missing 'path' to audio
  file","ok":false}`, so a malformed call won't even reach the (missing) engine.
* **`position` is what makes a source spatial.** Include it and you get an `AudioSource::spatial(clip,
  max_distance)` with quadratic distance attenuation relative to an `AudioListener`; omit it and you get a
  global source. `max_distance` defaults to `50.0`, `volume` to `1.0`, `loop` to `false`.
* **`/audio/stop` flips the source's `playing` flag; it doesn't despawn the entity.** Stopping an unknown
  entity returns `{"ok":false,"message":"Entity 7 not found"}`.
* **Buses, reverb, and occlusion exist beyond these routes.** `Master`/`Music`/`Sfx`/`Voice`/`Ui` buses
  with per-bus volume, `ReverbZone`s, `AudioOcclusion` line-of-sight muffling, and a concurrency cap live in
  `euca-audio` and apply once an engine is registered.
* **The engine is fork-safe.** It's shared as `Arc<Mutex<AudioEngine>>`, so a [forked
  world](/concepts/forks) reuses the same engine rather than cloning a device handle.

## Endpoints

| Method | Path          | Description                                                                          |
| ------ | ------------- | ------------------------------------------------------------------------------------ |
| `POST` | `/audio/play` | Load a clip and spawn an `AudioSource` — global, or spatial when `position` is given |
| `POST` | `/audio/stop` | Stop a playing source by `entity_id` (sets `playing` false)                          |
| `GET`  | `/audio/list` | List active sources — clip, volume, and spatial/playing/looping flags                |

To enable these end to end, register an `AudioEngine` on a host with an audio device — see
[Hosting & deployment](/systems/hosting-deployment) for the host-side resource pattern.

## Status

* **Shipped** — clip loading, global and spatial playback, distance attenuation, bus routing with per-bus
  volume, sound concurrency, fading, reverb zones, and occlusion.
* **Requires** — a host with an OS audio device and a registered `AudioEngine`; the reference headless
  server returns `AudioEngine not initialized`.

<Card title="Audio endpoints" icon="volume-high" href="/api-reference/introduction" horizontal>
  Play, stop, and list endpoints, with request and response schemas.
</Card>
