audio-player

Use this skill when the user asks to "aggiungi audio", "musica di sottofondo", "spatial audio", "AudioPlayer", "sound in scena", "carica audio", or wants to add audio to a Three.js/R3F scene.

AudioPlayer — Audio in scena R3F

File da creare

src/components/AudioPlayer.tsx

Componente completo

// src/components/AudioPlayer.tsx
import { useEffect, useRef } from 'react'
import { useThree } from '@react-three/fiber'
import * as THREE from 'three'

interface AudioPlayerProps {
  url: string
  volume?: number
  loop?: boolean
  autoplay?: boolean
  spatial?: boolean
  position?: [number, number, number]
  refDistance?: number
}

export function AudioPlayer({
  url,
  volume = 0.5,
  loop = true,
  autoplay = true,
  spatial = false,
  position = [0, 1, 0],
  refDistance = 1,
}: AudioPlayerProps) {
  const { camera } = useThree()
  const listenerRef = useRef<THREE.AudioListener | null>(null)
  const soundRef = useRef<THREE.Audio | THREE.PositionalAudio | null>(null)

  useEffect(() => {
    const listener = new THREE.AudioListener()
    camera.add(listener)
    listenerRef.current = listener

    const sound: THREE.Audio | THREE.PositionalAudio = spatial
      ? new THREE.PositionalAudio(listener)
      : new THREE.Audio(listener)

    const loader = new THREE.AudioLoader()
    loader.load(url, (buffer) => {
      sound.setBuffer(buffer)
      sound.setLoop(loop)
      sound.setVolume(volume)
      if (spatial && sound instanceof THREE.PositionalAudio) {
        sound.setRefDistance(refDistance)
      }
      if (autoplay) sound.play()
    })

    soundRef.current = sound

    return () => {
      if (soundRef.current?.isPlaying) soundRef.current.stop()
      camera.remove(listener)
      listener.context.close()
    }
  }, [url])

  if (!spatial) return null

  return (
    <group position={position}>
      <primitive object={soundRef.current ?? new THREE.Object3D()} />
    </group>
  )
}

Uso in scena

Background ambient (non spaziale)

import { AudioPlayer } from '../components/AudioPlayer'

// Dentro la scena:
<AudioPlayer
  url="/audio/ambient.mp3"
  volume={0.4}
  loop
  autoplay
/>

Spatial audio (suono posizionato nello spazio)

<AudioPlayer
  url="/audio/effect.mp3"
  spatial
  position={[2, 1, -1]}
  refDistance={2}
  volume={0.8}
/>

File audio

Metti i file in public/audio/:

public/
└── audio/
    ├── ambient.mp3
    ├── ambient.ogg   ← fallback per Safari
    └── effect.mp3

Formati supportati

FormatoSupporto
.mp3✅ universale
.ogg✅ Chrome/Firefox
.wav✅ ma file grandi
.webm✅ Chrome

Autoplay browser policy

I browser bloccano l'autoplay audio senza interazione utente. Soluzione: avvia il suono su click/tap:

const [started, setStarted] = useState(false)

// Nel DOM (fuori dal Canvas):
<button onClick={() => setStarted(true)}>Enter</button>

// In scena:
{started && <AudioPlayer url="/audio/ambient.mp3" autoplay />}

Hook useAudio (controllo esterno)

Per play/pause/volume dall'esterno del componente, usa useRef passato come prop o uno store Zustand.