Oyunlar için Web Audio API
İyi sesler bir oyunu dönüştürür. Sessiz bir oyun cansız hissettirir. Web Audio API; düşük gecikmeli ses efektleri, müzik çalma ve hatta 3B uzamsal ses özelliklerini doğrudan tarayıcıda kullanmanızı sağlar.
Deneyin (sentezlenmiş sesleri duymak için bir düğmeye tıklayın):
Otomatik Oynatma Sorunu
Her şeyden önce otomatik oynatma kısıtlamalarını anlamanız gerekir. Tarayıcılar, kullanıcı sayfanızla etkileşime geçene kadar sesi engeller. Bu iyi bir şeydir (kimse sayfa yüklenir yüklenmez ses yayınına başlayan siteler istemez), ancak sesi bir tıklama, dokunma veya tuşa basma işlemine yanıt olarak başlatmanız gerektiği anlamına gelir.
let audioCtx = null
function initAudio() {
if (!audioCtx) {
audioCtx = new AudioContext()
}
if (audioCtx.state === 'suspended') {
audioCtx.resume()
}
return audioCtx
}
// Initialize on first interaction
document.addEventListener('click', () => initAudio(), { once: true })
document.addEventListener('keydown', () => initAudio(), { once: true })Bundan önce ses çalmayı denerseniz işlem hiçbir uyarı vermeden başarısız olur veya AudioContext askıda kalır. Bu durum birçok geliştiriciyi hazırlıksız yakalar.
Sesleri Yükleme
Ses dosyalarının oynatılmadan önce kodlarının çözülmesi gerekir. Dosyayı alın, kodunu çözün ve yeniden kullanmak üzere arabelleği saklayın:
async function loadSound(url) {
const response = await fetch(url)
const arrayBuffer = await response.arrayBuffer()
const audioBuffer = await audioCtx.decodeAudioData(arrayBuffer)
return audioBuffer
}Kod çözme zaman alır; bu nedenle tüm seslerinizi çalmanız gerektiği anda değil, yükleme ekranı sırasında yükleyin.
Sesleri Çalma
Bir sesi çalmak için bir arabellek kaynağı oluşturun, ses seviyesini kontrol etmek üzere bunu bir kazanç düğümüne bağlayın ve başlatın:
function playSound(buffer, volume = 1) {
const source = audioCtx.createBufferSource()
source.buffer = buffer
const gainNode = audioCtx.createGain()
gainNode.gain.value = volume
source.connect(gainNode)
gainNode.connect(audioCtx.destination)
source.start(0)
return source
}Arabellek kaynakları tek kullanımlıktır. Bir kaynak oynatmayı bitirdikten sonra onu yeniden başlatamazsınız. Her oynatmada yeni bir tane oluşturun. Bu, kaynak israfı gibi görünse de API bu şekilde çalışır ve tarayıcılar buna göre optimize edilmiştir.
Ses Yöneticisi
Gerçek bir oyun için her şeyi bir yönetici sınıfında toplayın:
class SoundManager {
constructor() {
this.ctx = null
this.sounds = new Map()
this.masterGain = null
}
init() {
this.ctx = new AudioContext()
this.masterGain = this.ctx.createGain()
this.masterGain.connect(this.ctx.destination)
}
async load(name, url) {
const response = await fetch(url)
const buffer = await this.ctx.decodeAudioData(await response.arrayBuffer())
this.sounds.set(name, buffer)
}
play(name, volume = 1) {
const buffer = this.sounds.get(name)
if (!buffer) return
const source = this.ctx.createBufferSource()
source.buffer = buffer
const gain = this.ctx.createGain()
gain.gain.value = volume
source.connect(gain)
gain.connect(this.masterGain)
source.start(0)
return source
}
setMasterVolume(v) {
this.masterGain.gain.value = v
}
}Ana kazanç düğümü, genel bir ses seviyesi kaydırıcısı uygulamanızı sağlar. Tüm sesler bu düğüm üzerinden yönlendirilir.
Arka Plan Müziği
Müzik için döngüye alma ve durdurma ya da kademeli olarak kısma özellikleri gerekir:
class MusicPlayer {
constructor(ctx, masterGain) {
this.ctx = ctx
this.masterGain = masterGain
this.currentTrack = null
this.gain = ctx.createGain()
this.gain.connect(masterGain)
}
play(buffer, volume = 0.5) {
this.stop()
this.currentTrack = this.ctx.createBufferSource()
this.currentTrack.buffer = buffer
this.currentTrack.loop = true
this.currentTrack.connect(this.gain)
this.gain.gain.value = volume
this.currentTrack.start(0)
}
stop() {
if (this.currentTrack) {
this.currentTrack.stop()
this.currentTrack = null
}
}
fadeOut(duration = 1) {
this.gain.gain.linearRampToValueAtTime(0, this.ctx.currentTime + duration)
}
}linearRampToValueAtTime yöntemi, ani kesintiler yerine yumuşak geçişler sağlar.
Uzamsal Ses
3B oyunlarda sesleri uzayda konumlandırabilirsiniz. Sesler dinleyiciden uzaklaştıkça daha kısık hâle gelir:
function playSpatialSound(buffer, x, y, z) {
const source = audioCtx.createBufferSource()
source.buffer = buffer
const panner = audioCtx.createPanner()
panner.panningModel = 'HRTF'
panner.distanceModel = 'inverse'
panner.refDistance = 1
panner.maxDistance = 100
panner.positionX.value = x
panner.positionY.value = y
panner.positionZ.value = z
source.connect(panner)
panner.connect(audioCtx.destination)
source.start(0)
}
function updateListener(x, y, z, fx, fy, fz) {
const listener = audioCtx.listener
listener.positionX.value = x
listener.positionY.value = y
listener.positionZ.value = z
listener.forwardX.value = fx
listener.forwardY.value = fy
listener.forwardZ.value = fz
listener.upX.value = 0
listener.upY.value = 1
listener.upZ.value = 0
}Her karede kamera veya oyuncu konumuyla updateListener işlevini çağırın. Kaydırıcı, mesafe ve yöne göre ses seviyesini ve stereo kaydırmayı otomatik olarak ayarlar.
Hızla Tekrarlanan Sesler
Saniyede birçok kez çalınan seslerde (silah sesleri, ayak sesleri) üst üste binen örneklerin birikmesini istemezsiniz. Basit bir havuz kalıbı yardımcı olur:
class SoundPool {
constructor(ctx, buffer, size = 8) {
this.sources = []
this.index = 0
this.ctx = ctx
this.buffer = buffer
this.size = size
}
play(volume = 1) {
const source = this.ctx.createBufferSource()
source.buffer = this.buffer
const gain = this.ctx.createGain()
gain.gain.value = volume
source.connect(gain)
gain.connect(this.ctx.destination)
source.start(0)
this.index = (this.index + 1) % this.size
}
}Havuz, aynı sesin aynı anda kaç örneğinin çalınabileceğini sınırlar. Sınıra ulaştığınızda en eski örnek değiştirilir.
Ses Biçimleri
Farklı biçimler, farklı durumlarda daha iyi sonuç verir:
MP3 her yerde çalışır ve müzik için uygundur. OGG Vorbis, aynı dosya boyutunda daha iyi kalite sunar. Safari uzun süre bu biçimi desteklemeyen tek büyük tarayıcıydı ancak Safari 18.4 ile yerel Ogg Vorbis oynatma desteği eklendi; dolayısıyla güncel tüm büyük tarayıcılar artık bu biçimi destekliyor. Eski Safari sürümlerini önemsiyorsanız yine de yedek olarak MP3 sunmalısınız. AAC/M4A, Apple cihazlarında iyi çalışır. WAV sıkıştırılmamıştır ve büyüktür ancak kodu anında çözülür; bu da dosya boyutunu karşılayabildiğiniz kısa ses efektleri için uygundur. WebM/Opus en iyi kalite-boyut oranını sunar ancak yalnızca modern tarayıcılarda çalışır.
En geniş uyumluluk için yedek biçimler sunun:
const audioUrl = canPlayOgg() ? 'sound.ogg' : 'sound.mp3'Kullanışlı İpuçları
Perde değişimi, tekrarlanan seslerin daha az robotik duyulmasını sağlar:
function playWithPitchVariation(buffer, variance = 0.1) {
const source = audioCtx.createBufferSource()
source.buffer = buffer
source.playbackRate.value = 1 + (Math.random() - 0.5) * variance
source.connect(audioCtx.destination)
source.start(0)
}Böylece her silah veya ayak sesi biraz farklı duyulur.
Ses bastırma, diyaloglar veya önemli sesler sırasında müziğin seviyesini düşürür:
function duckMusic(musicGain, duration = 0.3) {
musicGain.gain.linearRampToValueAtTime(0.2, audioCtx.currentTime + duration)
}
function unduckMusic(musicGain, duration = 0.3) {
musicGain.gain.linearRampToValueAtTime(1, audioCtx.currentTime + duration)
}iOS'taki Dikkat Edilmesi Gerekenler
iOS, diğer platformlara göre daha sıkı ses kısıtlamalarına sahiptir. AudioContext bir kullanıcı hareketi sırasında oluşturulmalı ve sürdürülmelidir. Bazı iOS sürümleri ayrıca yalnızca bağlamı oluşturmanızı değil, kullanıcı hareketi sırasında bir ses (sessiz bile olsa) çalmanızı gerektirir. Ses masaüstünde çalışıyor ancak iOS'ta çalışmıyorsa nedeni muhtemelen budur.
Diğer Kaynaklar
Hızlı yüklenen bir web oyunu yayınlayın, ses dâhil varlık sıkıştırmayı ele alır.
Mobil uyumlu web oyunları, iOS'a özgü sorunlar hakkında daha fazla bilgi içerir.
Çevrimdışı oyunlar için PWA, çevrimdışı oynatma için ses dosyalarının nasıl önbelleğe alınacağını gösterir.
Oyun sesleri için Tone.js, daha üst düzey bir kütüphane kullanarak prosedürel ses üretimini ve müzik sistemlerini ele alır.
Ücretsiz Oyun Varlıkları Nerede Bulunur?, Freesound ve Poly Haven gibi ücretsiz ses efekti ve müzik kaynaklarını listeler.
Harici Kaynaklar
- MDN: Web Audio API — eksiksiz API referansı
- MDN: AudioContext — ses bağlamlarını oluşturma ve yönetme
- Web Audio API belirtimi — W3C belirtimi