audio-video

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Audio and Video in Decentraland

Decentraland 中的音频与视频

Audio Source (Sound Effects & Music)

音频源(音效与音乐)

Play audio clips from files:
typescript
import { engine, Transform, AudioSource } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'

const speaker = engine.addEntity()
Transform.create(speaker, { position: Vector3.create(8, 1, 8) })

AudioSource.create(speaker, {
  audioClipUrl: 'sounds/music.mp3',
  playing: true,
  loop: true,
  volume: 0.5,   // 0 to 1
  pitch: 1.0     // Playback speed (0.5 = half speed, 2.0 = double)
})
从文件播放音频片段:
typescript
import { engine, Transform, AudioSource } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'

const speaker = engine.addEntity()
Transform.create(speaker, { position: Vector3.create(8, 1, 8) })

AudioSource.create(speaker, {
  audioClipUrl: 'sounds/music.mp3',
  playing: true,
  loop: true,
  volume: 0.5,   // 0 to 1
  pitch: 1.0     // Playback speed (0.5 = half speed, 2.0 = double)
})

Supported Formats

支持的格式

  • .mp3
    (recommended)
  • .ogg
  • .wav
  • .mp3
    (推荐)
  • .ogg
  • .wav

File Organization

文件组织结构

project/
├── sounds/
│   ├── click.mp3
│   ├── background-music.mp3
│   └── explosion.ogg
├── src/
│   └── index.ts
└── scene.json
project/
├── sounds/
│   ├── click.mp3
│   ├── background-music.mp3
│   └── explosion.ogg
├── src/
│   └── index.ts
└── scene.json

Play/Stop/Toggle

播放/停止/切换

typescript
// Play
AudioSource.getMutable(speaker).playing = true

// Stop
AudioSource.getMutable(speaker).playing = false

// Toggle
const audio = AudioSource.getMutable(speaker)
audio.playing = !audio.playing
typescript
// Play
AudioSource.getMutable(speaker).playing = true

// Stop
AudioSource.getMutable(speaker).playing = false

// Toggle
const audio = AudioSource.getMutable(speaker)
audio.playing = !audio.playing

Play on Click

点击播放

typescript
import { pointerEventsSystem, InputAction } from '@dcl/sdk/ecs'

const button = engine.addEntity()
// ... set up transform and mesh ...

const audioEntity = engine.addEntity()
Transform.create(audioEntity, { position: Vector3.create(8, 1, 8) })
AudioSource.create(audioEntity, {
  audioClipUrl: 'sounds/click.mp3',
  playing: false,
  loop: false,
  volume: 0.8
})

pointerEventsSystem.onPointerDown(
  { entity: button, opts: { button: InputAction.IA_POINTER, hoverText: 'Play sound' } },
  () => {
    // Reset and play
    const audio = AudioSource.getMutable(audioEntity)
    audio.playing = false
    audio.playing = true
  }
)
typescript
import { pointerEventsSystem, InputAction } from '@dcl/sdk/ecs'

const button = engine.addEntity()
// ... set up transform and mesh ...

const audioEntity = engine.addEntity()
Transform.create(audioEntity, { position: Vector3.create(8, 1, 8) })
AudioSource.create(audioEntity, {
  audioClipUrl: 'sounds/click.mp3',
  playing: false,
  loop: false,
  volume: 0.8
})

pointerEventsSystem.onPointerDown(
  { entity: button, opts: { button: InputAction.IA_POINTER, hoverText: 'Play sound' } },
  () => {
    // Reset and play
    const audio = AudioSource.getMutable(audioEntity)
    audio.playing = false
    audio.playing = true
  }
)

Audio Streaming

音频流

Stream audio from a URL (radio, live streams):
typescript
import { engine, Transform, AudioStream } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'

const radio = engine.addEntity()
Transform.create(radio, { position: Vector3.create(8, 1, 8) })

AudioStream.create(radio, {
  url: 'https://example.com/stream.mp3',
  playing: true,
  volume: 0.3
})
从URL流式传输音频(电台、直播流):
typescript
import { engine, Transform, AudioStream } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'

const radio = engine.addEntity()
Transform.create(radio, { position: Vector3.create(8, 1, 8) })

AudioStream.create(radio, {
  url: 'https://example.com/stream.mp3',
  playing: true,
  volume: 0.3
})

Video Player

视频播放器

Play video on a surface:
typescript
import { engine, Transform, VideoPlayer, Material, MeshRenderer } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'

// Create a screen
const screen = engine.addEntity()
Transform.create(screen, {
  position: Vector3.create(8, 3, 15.9),
  scale: Vector3.create(8, 4.5, 1)  // 16:9 ratio
})
MeshRenderer.setPlane(screen)

// Add video player
VideoPlayer.create(screen, {
  src: 'https://example.com/video.mp4',
  playing: true,
  loop: true,
  volume: 0.5,
  playbackRate: 1.0,
  position: 0  // Start time in seconds
})

// Create video texture
const videoTexture = Material.Texture.Video({ videoPlayerEntity: screen })

// Basic material (recommended — better performance)
Material.setBasicMaterial(screen, {
  texture: videoTexture
})
在平面上播放视频:
typescript
import { engine, Transform, VideoPlayer, Material, MeshRenderer } from '@dcl/sdk/ecs'
import { Vector3 } from '@dcl/sdk/math'

// Create a screen
const screen = engine.addEntity()
Transform.create(screen, {
  position: Vector3.create(8, 3, 15.9),
  scale: Vector3.create(8, 4.5, 1)  // 16:9 ratio
})
MeshRenderer.setPlane(screen)

// Add video player
VideoPlayer.create(screen, {
  src: 'https://example.com/video.mp4',
  playing: true,
  loop: true,
  volume: 0.5,
  playbackRate: 1.0,
  position: 0  // Start time in seconds
})

// Create video texture
const videoTexture = Material.Texture.Video({ videoPlayerEntity: screen })

// Basic material (recommended — better performance)
Material.setBasicMaterial(screen, {
  texture: videoTexture
})

Video Controls

视频控制

typescript
// Play
VideoPlayer.getMutable(screen).playing = true

// Pause
VideoPlayer.getMutable(screen).playing = false

// Change volume
VideoPlayer.getMutable(screen).volume = 0.8

// Change source
VideoPlayer.getMutable(screen).src = 'https://example.com/other.mp4'
typescript
// Play
VideoPlayer.getMutable(screen).playing = true

// Pause
VideoPlayer.getMutable(screen).playing = false

// Change volume
VideoPlayer.getMutable(screen).volume = 0.8

// Change source
VideoPlayer.getMutable(screen).src = 'https://example.com/other.mp4'

Enhanced Video Material (PBR)

增强型视频材质(PBR)

For a brighter, emissive video screen:
typescript
import { Color3 } from '@dcl/sdk/math'

const videoTexture = Material.Texture.Video({ videoPlayerEntity: screen })
Material.setPbrMaterial(screen, {
  texture: videoTexture,
  roughness: 1.0,
  specularIntensity: 0,
  metallic: 0,
  emissiveTexture: videoTexture,
  emissiveIntensity: 0.6,
  emissiveColor: Color3.White()
})
创建更明亮、自发光的视频屏幕:
typescript
import { Color3 } from '@dcl/sdk/math'

const videoTexture = Material.Texture.Video({ videoPlayerEntity: screen })
Material.setPbrMaterial(screen, {
  texture: videoTexture,
  roughness: 1.0,
  specularIntensity: 0,
  metallic: 0,
  emissiveTexture: videoTexture,
  emissiveIntensity: 0.6,
  emissiveColor: Color3.White()
})

Video Events

视频事件

Monitor video playback state:
typescript
import { videoEventsSystem, VideoState } from '@dcl/sdk/ecs'

videoEventsSystem.registerVideoEventsEntity(screen, (videoEvent) => {
  switch (videoEvent.state) {
    case VideoState.VS_PLAYING:
      console.log('Video started playing')
      break
    case VideoState.VS_PAUSED:
      console.log('Video paused')
      break
    case VideoState.VS_READY:
      console.log('Video ready to play')
      break
    case VideoState.VS_ERROR:
      console.log('Video error occurred')
      break
  }
})
监控视频播放状态:
typescript
import { videoEventsSystem, VideoState } from '@dcl/sdk/ecs'

videoEventsSystem.registerVideoEventsEntity(screen, (videoEvent) => {
  switch (videoEvent.state) {
    case VideoState.VS_PLAYING:
      console.log('Video started playing')
      break
    case VideoState.VS_PAUSED:
      console.log('Video paused')
      break
    case VideoState.VS_READY:
      console.log('Video ready to play')
      break
    case VideoState.VS_ERROR:
      console.log('Video error occurred')
      break
  }
})

Spatial Audio

空间音频

Audio in Decentraland is spatial by default — it gets louder as the player approaches the audio source entity and quieter as they move away. The position is determined by the entity's
Transform
.
To make audio non-spatial (same volume everywhere), there's no built-in flag — keep the volume low and place the audio at the scene center.
Decentraland 中的音频默认是空间化的——玩家靠近音频源实体时声音会变大,远离时则变小。位置由实体的
Transform
组件决定。
若要使音频非空间化(音量保持一致),目前没有内置开关——可以将音量调低,并将音频源放置在场景中心。

Free Audio Files

免费音频文件

Always check the audio catalog before creating placeholder sound file references. It contains 50 free sounds from the Creator Hub asset packs.
Read
{baseDir}/../../context/audio-catalog.md
for music tracks (ambient, dance, medieval, sci-fi, etc.), ambient sounds (birds, city, factory, etc.), interaction sounds (buttons, doors, levers, chests), sound effects (explosions, sirens, bells), and game mechanic sounds (win/lose, heal, respawn, damage).
To use a catalog sound:
bash
undefined
在创建占位符音频文件引用前,请务必查看音频目录。该目录包含来自创作者中心资源包的50个免费音频文件。
阅读
{baseDir}/../../context/audio-catalog.md
获取各类音乐曲目(氛围音、舞曲、中世纪风格、科幻风格等)、环境音效(鸟鸣、城市、工厂等)、交互音效(按钮、门、杠杆、宝箱)、特效音(爆炸、警报、钟声)以及游戏机制音效(胜利/失败、治疗、重生、受伤)。
使用目录中的音频文件:
bash
undefined

Download from catalog

Download from catalog

```typescript
// Reference in code — must be a local file path
AudioSource.create(entity, { audioClipUrl: 'sounds/ambient_1.mp3', playing: true, loop: true })
```typescript
// Reference in code — must be a local file path
AudioSource.create(entity, { audioClipUrl: 'sounds/ambient_1.mp3', playing: true, loop: true })

How to suggest audio

如何推荐音频

  1. Read the audio catalog file
  2. Search for sounds matching the user's description/theme
  3. Suggest specific sounds with download commands
  4. Download selected sounds into the scene's
    sounds/
    directory
  5. Reference them in code with local paths
Important:
AudioSource
only works with local files. Never use external URLs for the
audioClipUrl
field. Always download audio into
sounds/
first.
  1. 阅读音频目录文件
  2. 搜索符合用户描述/主题的音频
  3. 推荐具体的音频文件并提供下载命令
  4. 将选中的音频下载到场景的
    sounds/
    目录中
  5. 在代码中使用本地路径引用这些音频
重要提示
AudioSource
仅支持本地文件。请勿在
audioClipUrl
字段中使用外部URL。请务必先将音频下载到
sounds/
目录中。

Video State Polling

视频状态轮询

Check video playback state programmatically:
typescript
import { videoEventsSystem, VideoState } from '@dcl/sdk/ecs'

engine.addSystem(() => {
  const state = videoEventsSystem.getVideoState(videoEntity)
  if (state) {
    console.log('Video state:', state.state) // VideoState.VS_PLAYING, VS_PAUSED, etc.
    console.log('Current time:', state.currentOffset)
  }
})
通过编程方式检查视频播放状态:
typescript
import { videoEventsSystem, VideoState } from '@dcl/sdk/ecs'

engine.addSystem(() => {
  const state = videoEventsSystem.getVideoState(videoEntity)
  if (state) {
    console.log('Video state:', state.state) // VideoState.VS_PLAYING, VS_PAUSED, etc.
    console.log('Current time:', state.currentOffset)
  }
})

Audio Playback Events

音频播放事件

Use the
AudioEvent
component to detect audio state changes:
typescript
import { AudioEvent } from '@dcl/sdk/ecs'

engine.addSystem(() => {
  const event = AudioEvent.getOrNull(audioEntity)
  if (event) {
    console.log('Audio state:', event.state) // playing, paused, finished
  }
})
使用
AudioEvent
组件检测音频状态变化:
typescript
import { AudioEvent } from '@dcl/sdk/ecs'

engine.addSystem(() => {
  const event = AudioEvent.getOrNull(audioEntity)
  if (event) {
    console.log('Audio state:', event.state) // playing, paused, finished
  }
})

Permission for External Media

外部媒体权限

External audio/video URLs require the
ALLOW_MEDIA_HOSTNAMES
permission in scene.json:
json
{
  "requiredPermissions": ["ALLOW_MEDIA_HOSTNAMES"],
  "allowedMediaHostnames": ["stream.example.com", "cdn.example.com"]
}
外部音频/视频URL需要在scene.json中配置
ALLOW_MEDIA_HOSTNAMES
权限:
json
{
  "requiredPermissions": ["ALLOW_MEDIA_HOSTNAMES"],
  "allowedMediaHostnames": ["stream.example.com", "cdn.example.com"]
}

Multiple Video Surfaces

多视频屏幕

Share one VideoPlayer across multiple screens by referencing the same
videoPlayerEntity
:
typescript
Material.setPbrMaterial(screen1, {
  texture: Material.Texture.Video({ videoPlayerEntity: videoEntity })
})
Material.setPbrMaterial(screen2, {
  texture: Material.Texture.Video({ videoPlayerEntity: videoEntity })
})
通过引用同一个
videoPlayerEntity
,在多个屏幕上共享一个VideoPlayer:
typescript
Material.setPbrMaterial(screen1, {
  texture: Material.Texture.Video({ videoPlayerEntity: videoEntity })
})
Material.setPbrMaterial(screen2, {
  texture: Material.Texture.Video({ videoPlayerEntity: videoEntity })
})

Video Limits & Tips

视频限制与技巧

  • Simultaneous videos: 1 in preview, 5 in Explorer, 10 max across the scene
  • Distance-based control: Pause video when player is far away to save bandwidth
  • Supported formats:
    .mp4
    (H.264),
    .webm
    , HLS (
    .m3u8
    ) for live streaming
  • Live streaming: Use HLS (
    .m3u8
    ) URLs — most reliable across clients
  • 同时播放的视频数量:预览环境中为1个,Explorer中为5个,场景中最多10个
  • 基于距离的控制:玩家远离时暂停视频以节省带宽
  • 支持的格式
    .mp4
    (H.264)、
    .webm
    、用于直播的HLS(
    .m3u8
  • 直播流:使用HLS(
    .m3u8
    )URL——在各客户端中兼容性最佳

Important Notes

重要注意事项

  • Audio files must be in the project's directory (relative paths from project root)
  • Video requires HTTPS URLs — HTTP won't work
  • Players must interact with the scene (click) before audio can play (browser autoplay policy)
  • Keep audio files small — large files increase scene load time
  • Use
    .mp3
    for music and
    .ogg
    for sound effects (smaller file sizes)
  • For live video streaming, use HLS (.m3u8) URLs when possible
  • 音频文件必须位于项目目录中(相对于项目根目录的相对路径)
  • 视频需要使用HTTPS URL——HTTP无法正常工作
  • 玩家必须与场景交互(点击)后才能播放音频(浏览器自动播放策略限制)
  • 音频文件体积请保持较小——大文件会增加场景加载时间
  • 音乐推荐使用
    .mp3
    格式,音效推荐使用
    .ogg
    格式(文件体积更小)
  • 若进行视频直播,请尽可能使用HLS(.m3u8)URL