5 bài trước, âm thanh luôn phát ra "ở giữa" — không có khái niệm trái/phải hay gần/xa. Bài này thêm chiều không gian: từ cân bằng trái-phải đơn giản (StereoPannerNode) tới định vị 3D đầy đủ mô phỏng cách tai người thật nghe hướng âm thanh (PannerNode + HRTF).

1. StereoPannerNode: Cân Bằng Trái/Phải Đơn Giản

audioContext.createStereoPanner() chỉ có đúng 1 tham số: pan, nhận giá trị từ -1 (hoàn toàn trái) tới 1 (hoàn toàn phải), 0 là chính giữa. Đây là dạng "không gian" đơn giản nhất — không mô phỏng khoảng cách, không mô phỏng chiều sâu, chỉ đơn thuần chia tỉ lệ âm lượng giữa 2 kênh loa.

ℹ️ Khi nào dùng StereoPannerNode thay vì PannerNode?
Nếu ứng dụng chỉ cần "kéo" âm thanh sang trái/phải (mixer nhạc, hiệu ứng UI đơn giản), StereoPannerNode nhẹ hơn nhiều về mặt tính toán và code ngắn gọn hơn hẳn. Chỉ cần PannerNode khi thực sự cần mô phỏng nguồn âm tồn tại ở 1 VỊ TRÍ trong không gian 3D (game, trải nghiệm VR/AR, mô phỏng môi trường thật).

2. PannerNode & Mô Hình HRTF: Không Gian 3D Thật Sự

audioContext.createPanner() định vị nguồn âm bằng toạ độ 3 chiều (positionX/Y/Z) trong không gian, kết hợp với vị trí và hướng của audioContext.listener (đại diện cho "tai" người nghe) để tính ra âm thanh cuối cùng. Thuộc tính panningModel quyết định thuật toán tính toán:

  • 'equalpower' (mặc định) — chỉ điều chỉnh âm lượng tương đối giữa 2 tai theo góc, tương tự StereoPannerNode nhưng có thêm khái niệm khoảng cách.
  • 'HRTF' (Head-Related Transfer Function) — áp bộ lọc được đo thực nghiệm trên hình dạng đầu/tai người mẫu, mô phỏng cả độ trễ thời gian đến 2 tai (ITD) lẫn khác biệt cường độ giữa 2 tai (ILD) — giúp não bộ định vị được cả hướng trước/sau/trên/dưới, không chỉ trái/phải.
🔬 Đào sâu: Vì sao chỉ chỉnh âm lượng 2 tai (ILD) không đủ để định vị "trước/sau"?
Một nguồn âm ở ngay trước mặt và một nguồn ở ngay sau gáy có thể tạo ra ILD gần như giống hệt nhau (cùng khoảng cách tới 2 tai). Não bộ con người phân biệt được 2 trường hợp này chủ yếu nhờ hình dạng vành tai lọc tần số cao khác nhau tuỳ hướng tới — đây chính xác là thứ HRTF mô phỏng (bằng bộ lọc đo thực nghiệm), còn ILD/ITD đơn thuần (equalpower) thì không thể tái tạo được hiệu ứng lọc theo hình dạng tai này.

3. Vị Trí & Hướng: Nguồn Âm và Listener

Cả nguồn âm (PannerNode) và người nghe (audioContext.listener) đều có toạ độ vị trí (positionX/Y/Z) và hướng nhìn (orientationX/Y/Z) — đây đều là AudioParam (từ Bài 1), có thể lập lịch mượt bằng setValueAtTime/linearRampToValueAtTime giống mọi tham số khác đã học:

panner-position.js
const panner = audioContext.createPanner();
panner.panningModel = 'HRTF';
panner.positionX.setValueAtTime(3, audioContext.currentTime); // 3 mét sang phải
panner.positionZ.setValueAtTime(-2, audioContext.currentTime); // 2 mét phía trước

// Vị trí/hướng người nghe — mặc định (0,0,0), nhìn theo trục -Z
const listener = audioContext.listener;
listener.positionX.setValueAtTime(0, audioContext.currentTime);
listener.forwardX.setValueAtTime(0, audioContext.currentTime);
listener.forwardZ.setValueAtTime(-1, audioContext.currentTime);

4. Mô Hình Suy Giảm Theo Khoảng Cách

PannerNode còn tự động giảm âm lượng theo khoảng cách qua 3 tham số: refDistance (khoảng cách "chuẩn" chưa suy giảm), maxDistance, và rolloffFactor (tốc độ suy giảm). Với distanceModel = 'inverse' (mặc định, giống vật lý thật của sóng âm cầu):

$$\text{gain} = \dfrac{\text{refDistance}}{\text{refDistance} + \text{rolloff} \times (\text{distance} - \text{refDistance})}$$

🕳️ Cạm bẫy thường gặp: Quên mô hình 3D dùng đơn vị "mét" trừu tượng
positionX/Y/Z không có đơn vị vật lý bắt buộc — bạn tự quyết định 1 đơn vị "khoảng cách" nghĩa là gì trong ứng dụng của mình. Nếu refDistance mặc định (1) không khớp với thang đo bạn đang dùng (vd toạ độ game tính bằng hàng trăm đơn vị pixel), âm lượng sẽ suy giảm gần như về 0 ngay ở khoảng cách rất gần — luôn chỉnh refDistance/ maxDistance khớp với đơn vị toạ độ thực tế của ứng dụng.

5. So Sánh StereoPannerNode vs PannerNode

Tiêu chí StereoPannerNode PannerNode
Số chiều không gian 1 (trái ↔ phải) 3 (x, y, z đầy đủ)
Mô phỏng khoảng cách? Không Có (distance model)
Mô hình HRTF? Không hỗ trợ Có (panningModel: 'HRTF')
Chi phí tính toán Rất thấp Cao hơn (đặc biệt với HRTF)

Sân chơi tương tác: Spatial Audio Playground

Phát 1 âm liên tục, chọn kiểu định vị, kéo slider hoặc bấm "Tự động xoay" để nguồn âm di chuyển quanh người nghe — nghe (đeo tai nghe để cảm nhận rõ nhất với chế độ HRTF) và quan sát trực quan vị trí trên canvas.

🎧 Sân chơi tương tác: Spatial Audio Playground

Kiểu định vị

Nhật ký

audio-spatial-stereo-live.js

Trắc nghiệm ôn tập

Câu 1: Khác biệt cơ bản nhất giữa StereoPannerNodePannerNode là gì?

Trắc nghiệm ôn tập

Câu 2: Mô hình HRTF giúp ích gì mà panningModel: 'equalpower' (mặc định) không làm được?

Trắc nghiệm ôn tập

Câu 3: Vì sao PannerNode cần biết cả vị trí của audioContext.listener chứ không chỉ vị trí của chính nguồn âm?

📖 Tài liệu tham khảo / References

Bài viết liên quan trong series

Bài 7: AudioWorklet & DSP Tuỳ Biến Bài 5: Phát & Xử Lý File/Mic Quay lại Lộ trình Series Web Audio API