NodeJS bindings for the ODIN SDK. Use for AI enhanced human interactions, content moderation and audio processing features in a backend.



Native Node.js bindings for the ODIN Voice SDK. Build powerful voice chat applications, recording bots, AI integrations, and real-time audio processing tools.
๐ Full Documentation | ๐ฌ Discord Community | ๐ฎ 4Players ODIN
---
- ๐๏ธ Real-time Voice Chat - Low-latency voice communication
- ๐ End-to-End Encryption - Built-in E2EE with OdinCipher
- ๐ค Bot Integration - Perfect for recording bots, AI assistants, and moderation tools
- ๐ Raw Audio Access - Get PCM audio data for processing, recording, or transcription
- ๐ Proximity Chat - 3D positional audio support
- โก High Performance - Native C++ bindings for maximum efficiency
- ๐ Diagnostics - Real-time connection and audio quality monitoring
---
``bash`
npm install @4players/odin-nodejs
This SDK includes prebuilt binaries for:
- macOS (x86_64 and arm64)
- Windows (x86_64)
- Linux (x86_64)
For other platforms, you'll need a C++ compiler. See node-gyp requirements.
---
Sign up at 4Players ODIN to get your free access key.
`javascript
import odin from '@4players/odin-nodejs';
const { OdinClient } = odin;
// Configuration - replace with your credentials
const accessKey = "__YOUR_ACCESS_KEY__";
const roomId = "my-room";
const userId = "user-123";
async function main() {
// Create client and generate token locally
const client = new OdinClient();
const token = client.generateToken(accessKey, roomId, userId);
// Create room using factory pattern
const room = client.createRoom(token);
// Set up event handlers
room.onJoined((event) => {
console.log(Joined room: ${event.roomId});My peer ID: ${event.ownPeerId}
console.log();Available media IDs: ${event.mediaIds}
console.log();
});
room.onPeerJoined((event) => {
console.log(Peer joined: ${event.peerId});
});
room.onPeerLeft((event) => {
console.log(Peer left: ${event.peerId});
});
// Join the room
room.join("https://gateway.odin.4players.io");
// Keep connection alive
process.on('SIGINT', () => {
room.close();
process.exit(0);
});
}
main();
`
---
The repository includes a ready-to-use Dockerfile that shows how to run the SDK inside a Linux/amd64 container. It installs @4players/odin-nodejs, is configured for the official ODIN gateway, and runs a lightweight connection test. No manual library-path tweaks are requiredโthe Linux prebuild now embeds $ORIGIN so the bundled libodin*.so files load automatically.
Build the example image:
`bash`
docker build --platform=linux/amd64 -t odin-nodejs-docker-example .
Run it by passing your access key (and optionally a room ID, user ID, or custom gateway):
`bash`
docker run --rm --platform=linux/amd64 \
-e ODIN_ACCESS_KEY="ATPClAXgmBgY1ryDk/kTC2Yhitf4fJSx95jpN3F9Xac3" \
-e ODIN_ROOM_ID="my-room" \
odin-nodejs-docker-example
Environment variables supported by the example:
- ODIN_ACCESS_KEY (required) โ access key used to mint tokens.ODIN_ROOM_ID
- (optional) โ defaults to odin-sdk-ci-test.ODIN_USER_ID
- (optional) โ auto-generates a random ID when omitted.ODIN_GATEWAY
- (optional) โ defaults to https://gateway.odin.4players.io.RUN_DURATION_MS
- (optional) โ how long to keep the connection open.
You can also use the Dockerfile as a starting point for your own servicesโreplace the provided sample script with your application logic.
---
The SDK provides typed event handlers for easy integration:
`javascriptState: ${event.state}
// Connection events
room.onConnectionStateChanged((event) => {
console.log(); // Connecting, Joined, Disconnected, etc.
});
room.onJoined((event) => {
// { roomId, ownPeerId, room, mediaIds }
});
room.onLeft((event) => {
// { reason }
});
// Peer events
room.onPeerJoined((event) => {
// { peerId, userId, userData, peer }
});
room.onPeerLeft((event) => {
// { peerId }
});
// Media events
room.onMediaStarted((event) => {
// { peerId, media }
});
room.onMediaStopped((event) => {
// { peerId, mediaId }
});
room.onMediaActivity((event) => {
// { peerId, mediaId, state } - Voice Activity Detection
});
// Messages
room.onMessageReceived((event) => {
// { senderPeerId, message }
});
// Audio data (for recording/processing)
room.onAudioDataReceived((data) => {
// { peerId, mediaId, samples16, samples32 }
});
`
---
Record audio from peers to WAV files:
`javascript
import odin from '@4players/odin-nodejs';
import wav from 'wav';
const { OdinClient } = odin;
// Configuration
const accessKey = "__YOUR_ACCESS_KEY__";
const roomId = "my-room";
const userId = "RecorderBot";
const recordings = {};
async function main() {
const client = new OdinClient();
const token = client.generateToken(accessKey, roomId, userId);
const room = client.createRoom(token);
room.onAudioDataReceived((data) => {
const { mediaId, peerId, samples16 } = data;
// Create recording file if needed
if (!recordings[mediaId]) {
recordings[mediaId] = new wav.FileWriter(recording_${peerId}.wav, {
channels: 2,
sampleRate: 48000,
bitDepth: 16
});
}
// Write audio samples
const buffer = Buffer.from(samples16.buffer, samples16.byteOffset, samples16.byteLength);
recordings[mediaId].write(buffer);
});
room.onMediaStopped((event) => {
if (recordings[event.mediaId]) {
recordings[event.mediaId].end();
delete recordings[event.mediaId];
}
});
room.join("https://gateway.odin.4players.io");
}
main();
`
---
The SDK provides two approaches for sending audio: a high-level API for convenience and a low-level API for full control.
The high-level API handles all the complexity automatically - media ID allocation, StartMedia RPC, and timing:
`javascript
import odin from '@4players/odin-nodejs';
const { OdinClient } = odin;
// Configuration
const accessKey = "__YOUR_ACCESS_KEY__";
const roomId = "my-room";
const userId = "AudioBot";
async function main() {
const client = new OdinClient();
const token = client.generateToken(accessKey, roomId, userId);
const room = client.createRoom(token);
// Wait for room join
const joinPromise = new Promise(resolve => room.onJoined(resolve));
room.join("https://gateway.odin.4players.io");
await joinPromise;
// Create audio stream and send audio with one line!
const media = room.createAudioStream(44100, 2);
// Send an MP3 file (auto-decodes and streams with correct timing)
await media.sendMP3('./music.mp3');
// Or send a WAV file
await media.sendWAV('./audio.wav');
// Or send a decoded AudioBuffer
// await media.sendBuffer(audioBuffer);
media.close();
room.close();
}
main();
`
For full control over audio transmission, use the low-level API:
`javascript
import odin from '@4players/odin-nodejs';
const { OdinClient } = odin;
import { encode } from '@msgpack/msgpack';
// Configuration
const accessKey = "__YOUR_ACCESS_KEY__";
const roomId = "my-room";
const userId = "AudioBot";
async function main() {
const client = new OdinClient();
const token = client.generateToken(accessKey, roomId, userId);
const room = client.createRoom(token);
room.onJoined(async (event) => {
// 1. Get media ID from the event
const mediaId = event.mediaIds[0];
// 2. Create audio stream
const media = room.createAudioStream(48000, 2);
// 3. Set the server-assigned media ID
media.setMediaId(mediaId);
// 4. Send StartMedia RPC to notify server
const rpc = encode([0, 1, "StartMedia", {
media_id: mediaId,
properties: { kind: "audio" }
}]);
room.sendRpc(new Uint8Array(rpc));
// 5. Send audio data in 20ms chunks
const chunkDurationMs = 20;
const samplesPerChunk = Math.floor(48000 chunkDurationMs / 1000) 2;
// Your audio data as Float32Array (interleaved stereo, range [-1, 1])
const audioChunk = new Float32Array(samplesPerChunk);
// ... fill with audio samples ...
media.sendAudioData(audioChunk);
// 6. When done, close
media.close();
});
room.join("https://gateway.odin.4players.io");
}
main();
`
See tests/sending-audio/ for complete examples of both APIs.
---
Enable encryption for secure voice communication:
`javascript
import odin from '@4players/odin-nodejs';
const { OdinClient, OdinCipher } = odin;
const client = new OdinClient();
const token = client.generateToken(accessKey, roomId, userId);
const room = client.createRoom(token);
// Create and configure cipher
const cipher = new OdinCipher();
cipher.setPassword(new TextEncoder().encode("shared-secret-password"));
// Apply cipher to room
room.setCipher(cipher);
room.join("https://gateway.odin.4players.io");
`
> โ ๏ธ All participants in a room must use the same cipher password to communicate.
`javascriptPeer ${peerId} encryption: ${status}
// Check if a peer's encryption matches ours
const status = cipher.getPeerStatus(peerId);
console.log();`
// Possible values: "encrypted", "mismatch", "unencrypted", "unknown"
---
Enable distance-based audio for spatial applications:
`javascript`
room.onJoined(() => {
// Set position scale (1 unit = 1 meter)
room.setPositionScale(1.0);
// Update your position
room.updatePosition(10.0, 0.0, 5.0); // x, y, z
});
---
Monitor connection quality and troubleshoot issues:
`javascriptConnection ID: ${connectionId}
room.onJoined(() => {
// Get connection identifier
const connectionId = room.getConnectionId();
console.log();
// Get detailed connection statistics
const stats = room.getConnectionStats();
if (stats) {
console.log(RTT: ${stats.rtt.toFixed(2)} ms);TX Loss: ${(stats.udpTxLoss * 100).toFixed(2)}%
console.log();RX Loss: ${(stats.udpRxLoss * 100).toFixed(2)}%
console.log();TX Bytes: ${stats.udpTxBytes}
console.log();RX Bytes: ${stats.udpRxBytes}
console.log();Congestion Events: ${stats.congestionEvents}
console.log();
}
// Get jitter statistics for an audio stream
const jitterStats = room.getJitterStats(mediaId);
if (jitterStats) {
console.log(Packets Total: ${jitterStats.packetsTotal});Packets Lost: ${jitterStats.packetsLost}
console.log();Packets Too Late: ${jitterStats.packetsArrivedTooLate}
console.log();`
}
});
---
| Method | Description |
|--------|-------------|
| generateToken(accessKey, roomId, userId) | Generate a room token locally |createRoom(token)
| | Create a room instance (recommended) |createRoomWithToken(token)
| | Alias for createRoom |
| Method | Description |
|--------|-------------|
| join(gateway, userData?) | Connect to the room |close()
| | Disconnect from the room |sendMessage(data, peerIds?)
| | Send a message to peers |updatePosition(x, y, z)
| | Update 3D position |setPositionScale(scale)
| | Set position scale factor |setCipher(cipher)
| | Enable E2EE |createAudioStream(sampleRate, channels)
| | Create audio output stream |getConnectionId()
| | Get connection identifier |getConnectionStats()
| | Get connection quality metrics |getJitterStats(mediaId)
| | Get audio jitter metrics |
| Property | Type | Description |
|----------|------|-------------|
| ownPeerId | number | Your peer ID |connected
| | boolean | Connection status |availableMediaIds
| | number[] | Available media IDs for audio streams |
| Method | Description |
|--------|-------------|
| setMediaId(mediaId) | Set server-assigned media ID |close()
| | Release the stream |sendAudioData(samples)
| | Send raw audio samples |sendMP3(filePath)
| | Stream an MP3 file (convenience) |sendWAV(filePath)
| | Stream a WAV file (convenience) |sendBuffer(audioBuffer)
| | Stream AudioBuffer (convenience) |
| Method | Description |
|--------|-------------|
| setPassword(password) | Set encryption password |getPeerStatus(peerId)
| | Get peer's encryption status |
| Event | Payload |
|-------|---------|
| ConnectionStateChanged | { state, message } |Joined
| | { roomId, ownPeerId, room, mediaIds } |Left
| | { reason } |PeerJoined
| | { peerId, userId, userData, peer } |PeerLeft
| | { peerId } |MediaStarted
| | { peerId, media } |MediaStopped
| | { peerId, mediaId } |MediaActivity
| | { peerId, mediaId, state } |MessageReceived
| | { senderPeerId, message } |AudioDataReceived
| | { peerId, mediaId, samples16, samples32 } |
---
| Feature | Node.js SDK | Web SDK |
|---------|-------------|---------|
| Platform | Node.js (server) | Browser |
| Performance | Native C++ | WebRTC/JavaScript |
| Raw Audio Access | โ
Full PCM data | โ ๏ธ Web Audio API |
| Use Cases | Bots, recording, AI | Client apps |
| E2EE | โ
OdinCipher | โ
OdinCipher |
The Node.js SDK is optimized for server-side use cases like:
- ๐๏ธ Audio recording bots
- ๐ค AI-powered voice assistants
- ๐ Speech-to-text transcription
- ๐ก๏ธ Content moderation
- ๐ Audio processing pipelines
---
Check the tests/ folder for complete examples:
- connection-test - Basic connection, events, and diagnostics
- audio-recording - Recording peer audio to WAV files
- sending-audio - Sending audio with both high-level and low-level APIs
---
If you encounter build errors, ensure you have the required tools:
`bashmacOS
xcode-select --install
$3
If you see "code signature not valid" errors:
`bash
cd node_modules/@4players/odin-nodejs/build/Debug
xattr -cr *.dylib
codesign -f -s - *.dylib
`$3
1. Verify your access key is correct
2. Check your network allows WebSocket connections
3. Ensure the token hasn't expired
---
Development
$3
This package includes prebuilt binaries for common platforms (macOS x64/arm64, Windows x64, Linux x64). If you need to build for a different platform or architecture, follow these steps:
#### 1. Install Build Requirements
You'll need a C++ compiler toolchain:
`bash
macOS
xcode-select --installUbuntu/Debian
sudo apt-get install build-essential python3Windows
npm install --global windows-build-tools
`#### 2. Download ODIN SDK Libraries
Download the ODIN SDK libraries from the official releases:
1. Download the appropriate archive for your platform from the release assets
2. Extract the libraries to the correct location:
| Platform | Architecture | Target Directory |
|----------|--------------|------------------|
| Linux | x64 |
libs/bin/linux/x64/ |
| Linux | arm64 | libs/bin/linux/arm64/ |
| Linux | ia32 | libs/bin/linux/ia32/ |
| macOS | Universal | libs/bin/macos/universal/ |
| Windows | x64 | libs/bin/windows/x64/ |
| Windows | ia32 | libs/bin/windows/ia32/ |The SDK archive contains these library files:
- Linux:
libodin_static.a, libodin.so, libodin_crypto_static.a, libodin_crypto.so
- macOS: libodin.dylib, libodin_crypto.dylib, libodin_static.a, libodin_crypto_static.a
- Windows: odin_static.lib, odin.dll, odin_crypto_static.lib, odin_crypto.dll#### 3. Build the Native Module
`bash
Build in debug mode
npm run build:debugBuild in release mode
npm run build:release
`#### 4. Verify the Build
`bash
node -e "const odin = require('./index.cjs'); console.log('ODIN SDK loaded:', !!odin.OdinClient);"
`$3
`
โโโ cppsrc/ # C++ native bindings source code
โโโ libs/
โ โโโ bin/ # ODIN SDK binaries (all platforms)
โ โ โโโ linux/ # Linux binaries (x64, arm64, ia32)
โ โ โโโ macos/ # macOS binaries (arm64, x64, universal)
โ โ โโโ windows/ # Windows binaries (x64, ia32)
โ โโโ include/ # ODIN SDK headers (odin.h, odin_crypto.h)
โโโ index.cjs # JavaScript wrapper
โโโ *.d.ts # TypeScript type definitions
โโโ tests/ # Example scripts
``---
We welcome contributions! Please see our Contributing Guide for details.
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Submit a pull request
---
- ๐ Documentation: docs.4players.io
- ๐ฌ Discord: Join our community
- ๐ง Email: support@4players.io
- ๐ Issues: GitHub Issues
---
MIT License - see LICENSE for details.
---
Made with โค๏ธ by 4Players GmbH