Package Exports
- @newgameplusinc/odyssey-official-audio-video-sdk
- @newgameplusinc/odyssey-official-audio-video-sdk/dist/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@newgameplusinc/odyssey-official-audio-video-sdk) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Odyssey Audio/Video SDK (MediaSoup + Web Audio)
This package exposes OdysseySpatialComms, a thin TypeScript client that glues together:
- MediaSoup SFU for ultra-low-latency audio/video routing
- Web Audio API for Apple-like spatial mixing via
SpatialAudioManager - Socket telemetry (position + direction) so every browser hears/see everyone exactly where they are in the 3D world
It mirrors the production SDK used by Odyssey V2 and ships ready-to-drop into any Web UI (Vue, React, plain JS).
Feature Highlights
- π One class to rule it all β
OdysseySpatialCommswires transports, producers, consumers, and room state. - π§ Accurate pose propagation β
updatePosition()streams listener pose to the SFU whileparticipant-position-updatedkeeps the local store in sync. - π§ Studio-grade spatial audio β each remote participant gets a dedicated Web Audio graph: denoiser β high-pass β low-pass β HRTF
PannerNodeβ adaptive gain β master compressor. - π₯ Camera-ready streams β video tracks are exposed separately so UI layers can render muted
<video>tags while audio stays inside Web Audio. - π EventEmitter contract β subscribe to
room-joined,consumer-created,participant-position-updated, etc., without touching Socket.IO directly.
Quick Start
import {
OdysseySpatialComms,
Direction,
Position,
} from "@newgameplusinc/odyssey-audio-video-sdk-dev";
const sdk = new OdysseySpatialComms("https://mediasoup-server.example.com");
// 1) Join a room
await sdk.joinRoom({
roomId: "demo-room",
userId: "user-123",
deviceId: "device-123",
position: { x: 0, y: 0, z: 0 },
direction: { x: 0, y: 1, z: 0 },
});
// 2) Produce local media
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
for (const track of stream.getTracks()) {
await sdk.produceTrack(track);
}
// 3) Handle remote tracks
sdk.on("consumer-created", async ({ participant, track }) => {
if (track.kind === "video") {
attachVideo(track, participant.participantId);
}
});
// 4) Keep spatial audio honest
sdk.updatePosition(currentPos, currentDir);
sdk.setListenerFromLSD(listenerPos, cameraPos, lookAtPos);Audio Flow (Server β Browser)
ββββββββββββββββ update-position ββββββββββββββββ pose + tracks ββββββββββββββββββββ
β Browser LSD β βββββββββββββββββββΆ β MediaSoup SFUβ βββββββββββββββββΆ β SDK Event Bus β
β (Unreal data)β β + Socket.IO β β (EventManager) β
ββββββββ¬ββββββββ ββββββββ¬ββββββββ ββββββββββββ¬βββββββββ
β β track + pose
β β βΌ
β ββββββββββΌβββββββββ ββββββββββββββββββββ
β audio RTP β consumer-createdβ β SpatialAudioMgr β
ββββββββββββββββββββββββββββΆβ setup per-user βββββββββββββββββββββββββ (Web Audio API) β
ββββββββββ¬βββββββββ β - Denoiser β
β β - HP / LP β
β β - HRTF Panner β
βΌ β - Gain + Comp β
Web Audio Graph ββββββββββββ¬ββββββββ
β β
βΌ βΌ
Listener ears (Left/Right) System OutputWeb Audio Algorithms
- Coordinate normalization β Unreal sends centimeters;
SpatialAudioManagerauto-detects large values and converts to meters once. - Orientation math β
setListenerFromLSD()builds forward/right/up vectors from camera/LookAt to keep the listener aligned with head movement. - Dynamic distance gain β
updateSpatialAudio()measures distance from listener β source and applies a smooth rolloff curve, so distant avatars fade to silence. - Noise handling β the AudioWorklet denoiser now runs an adaptive multi-band gate (per W3C AudioWorklet guidance) before the high/low-pass filters, stripping constant HVAC/fan noise even when the speaker is close.
How Spatial Audio Is Built
- Telemetry ingestion β each LSD packet is passed through
setListenerFromLSD(listenerPos, cameraPos, lookAtPos)so the Web Audio listener matches the playerβs real head/camera pose. - Per-participant node graph β when
consumer-createdyields a remote audio track,setupSpatialAudioForParticipant()spins up an isolated graph:MediaStreamSource β (optional) Denoiser Worklet β High-Pass β Low-Pass β Panner(HRTF) β Gain β Master Compressor. - Position + direction updates β every
participant-position-updatedevent callsupdateSpatialAudio(participantId, position, direction). The position feeds the pannerβs XYZ, while the direction vector sets the source orientation so voices project forward relative to avatar facing. - Distance-aware gain β the manager stores the latest listener pose and computes the Euclidean distance to each remote participant on every update. A custom rolloff curve adjusts gain before the compressor, giving the βsomeone on my left / far awayβ perception without blowing out master levels.
- Left/right rendering β because the panner uses
panningModel = "HRTF", browsers feed the processed signal into the userβs audio hardware with head-related transfer functions, producing natural interaural time/intensity differences.
Video Flow (Capture β Rendering)
ββββββββββββββββ produceTrack ββββββββββββββββ RTP ββββββββββββββββ
β getUserMedia β ββββββββββββββββΆ β MediaSoup SDKβ βββββββΆ β MediaSoup SFUβ
ββββββββ¬ββββββββ β (Odyssey) β ββββββββ¬ββββββββ
β ββββββββ¬ββββββββ β
β consumer-created β track β
βΌ βΌ β
ββββββββββββββββ ββββββββββββββββ β
β Vue/React UI β ββββββββββββββββ β SDK Event Bus β ββββββββββββββββ
β (muted video β β exposes media β
β elements) β β tracks β
ββββββββββββββββ ββββββββββββββββCore Classes
src/index.tsβOdysseySpatialComms(socket lifecycle, producers/consumers, event surface).src/MediasoupManager.tsβ transport helpers for produce/consume/resume.src/SpatialAudioManager.tsβ Web Audio orchestration (listener transforms, per-participant chains, denoiser, distance math).src/EventManager.tsβ lightweight EventEmitter used by the entire SDK.
Integration Checklist
- Instantiate once per page/tab and keep it in a store (Vuex, Redux, Zustand, etc.).
- Pipe LSD/Lap data from your rendering engine into
updatePosition()+setListenerFromLSD()at ~10 Hz. - Render videos muted β never attach remote audio tracks straight to DOM; let
SpatialAudioManagerown playback. - Push avatar telemetry back to Unreal so
remoteSpatialDatacan render minimaps/circles (see Odyssey V2sendMediaSoupParticipantsToUnreal). - Monitor logs β browser console shows
π§ SDK,π SDK, andποΈ [Spatial Audio]statements for every critical hop.
Server Contract (Socket.IO events)
| Event | Direction | Payload |
|---|---|---|
join-room |
client β server | {roomId, userId, deviceId, position, direction} |
room-joined |
server β client | RoomJoinedData (router caps, participants snapshot) |
update-position |
client β server | {participantId, conferenceId, position, direction} |
participant-position-updated |
server β client | {participantId, position, direction, mediaState} |
consumer-created |
server β client | {participantId, track(kind), position, direction} |
participant-media-state-updated |
server β client | {participantId, mediaState} |
Development Tips
- Run
pnpm install && pnpm buildinsidemediasoup-sdk-testto publish a fresh build. - Use
pnpm watchwhile iterating so TypeScript outputs live underdist/. - The SDK targets evergreen browsers; for Safari <16.4 you may need to polyfill AudioWorklets or disable the denoiser via
new SpatialAudioManager({ denoiser: { enabled: false } }).
Have questions or want to extend the SDK? Start with SpatialAudioManager β thatβs where most of the βreal-worldβ behavior (distance feel, stereo cues, denoiser) lives.