Skip to content

Audio Components

Tecs Audio provides two ECS components for attaching audio to entities: AudioListener and AudioSource.

AudioListener

A tag component marking an entity as the audio listener. The listener's position determines how spatial audio is perceived (volume falloff, panning). The audio system updates love.audio.setPosition() each frame based on the listener entity's Transform. All spatial audio attenuation is calculated relative to this position.

teal
local audio = require("tecs2d.audio")

-- Mark the player as the audio listener
world:spawn(
    tecs.builtins.Transform(0, 0),
    Player(),
    audio.AudioListener()
)

Requirements

  • Only one listener: Only the first entity with AudioListener is used each frame
  • Requires Transform: The entity must have a Transform component for positioning

AudioSource

A component for continuous or looping sounds attached to entities. Use this for sounds that should be emitted in world space (e.g., footsteps, engines, ambient emitters).

teal
local audio = require("tecs2d.audio")
local assets = require("tecs2d.assets")

local assetManager = world.resources[assets]
local engineHandle = assetManager:loadAudio("sounds/engine.wav", "static")

-- Positional sound (follows entity position)
world:spawn(
    tecs.builtins.Transform(100, 200),
    audio.AudioSource({
        handle = engineHandle,
        looping = true,
        refDistance = 50,
        maxDistance = 500
    })
)

-- Relative sound (always follows the listener, no attenuation)
world:spawn(
    audio.AudioSource({
        handle = musicHandle,
        group = "music",
        looping = true,
        relative = true
    })
)

Configuration

FieldTypeDefaultDescription
handleHandlerequiredAudio asset handle from assets:loadAudio()
groupstring"sfx"Sound group for volume control
volumenumber1.0Volume multiplier (0-1)
pitchnumber1.0Pitch multiplier (0-1)
pitchVariancenumber0Random pitch offset (e.g. 0.1 gives pitch +/- 0.1)
loopingbooleanfalseWhether the sound loops
playingbooleantrueWhether to start playing immediately
relativebooleanfalseIf true, sound follows the listener with no attenuation
refDistancenumber100Distance in pixels where volume begins to fade
maxDistancenumber1000Distance in pixels where the sound becomes silent

Positional vs Relative

Positional (default): The sound exists in world space. Volume and panning change based on the listener's distance and orientation, like hearing a car drive past you.

teal
-- Engine sound follows a vehicle
world:spawn(
    tecs.builtins.Transform(vehicleX, vehicleY),
    audio.AudioSource({
        handle = engineHandle,
        looping = true
    })
)

Relative: The sound is attached to the listener, like wearing headphones. It plays at full volume regardless of where the listener is in the world. Use for background music, UI sounds, or ambient audio.

teal
-- Background music (plays "in the listener's ears")
world:spawn(
    audio.AudioSource({
        handle = musicHandle,
        group = "music",
        looping = true,
        relative = true
    })
)

Attenuation Settings

Control how sound volume falls off with distance. Distances are measured in pixels, matching your Transform coordinates.

teal
audio.AudioSource({
    handle = soundHandle,
    refDistance = 50,   -- Full volume within 50px
    maxDistance = 500   -- Silent beyond 500px
})

The attenuation follows Love2D's default model (inverse distance clamped).

Controlling Playback

Modify the component to control playback:

teal
-- Get the component
local source = world:get(entityId, audio.AudioSource)

-- Stop playback
source.playing = false

-- Start playback
source.playing = true

-- Change volume
source.volume = 0.5

-- Change pitch (takes effect on next loop for looping sounds)
source.pitch = 1.2

Pitch Variance

Add random pitch variance to prevent repetitive sounds:

teal
audio.AudioSource({
    handle = footstepHandle,
    pitch = 1.0,
    pitchVariance = 0.1  -- Pitch will vary +/- 0.1
})

Pitch variance is applied each time the sound starts or loops (not while playing).