Depends on
Required for this file to work correctly.
About Sound Library
Give your server any sound you want. SoundLibrary is a server-side sound library for Rust: drop .ogg files into a folder and every sound becomes instantly playable — positioned in the world, attached to players or entities, looped, streamed through vanilla networking with zero client mod or workshop upload required.
No client downloads, no upload to a workshop, no restart to pick up a new file. The plugin watches its sound folder live, caches everything in server FileStorage and streams it to clients through the game's own file-request pipeline — the exact pipeline official custom content rides on. Whether you want a raid alarm that shakes the whole map, a private voice line only one player hears, or ambient sound zones that ride along a moving object, you wire it up in one API call.
A complete playback API (19 methods) turns audio into a building block for other plugins: events, NPCs, minigames, raid notifications, proximity ambience. SoundLibrary does the hard networking part; your plugins do the fun part.
Features
-
No client mod, no workshop — sounds are cached in server FileStorage and streamed to clients on demand through the game's own file-request pipeline (vanilla
sv_requestfile), exactly like official custom content. Everything your players hear comes from vanilla systems. - Zero-install for players — a client missing the sound simply requests it; the server answers through the same channel as official files, with the same caching, so the transfer happens exactly once per player per sound.
-
Drop-in sound loading — the plugin watches its data folder live: add, replace, rename or delete a
.oggand it loads or unloads within seconds. No restart, no plugin reload, no console commands to teach the server about new files. -
Single sounds & multi-part audio — a root
.oggfile is one sound; a first-level subfolder of numbered files (1.ogg,2.ogg,3.ogg…) is one long sound whose segments chain seamlessly in numeric order. Watcher events arriving out of order are re-sorted, so a growing folder always plays as one continuous track. - Positioned playback — play any sound at a world position for every connected player. One call, whole-server audio.
- Attached playback — parent a sound to a player, an entity, or a specific bone, so it follows its host wherever it goes. Entity playback is heard by that entity's network subscribers and not by the rest of the map — you get proximity-correct audio for free.
- Private playback — target a single player or connection: only they hear it. Ideal for personal voice lines, UI-style chimes or per-player cues without leaking to everyone else.
- Looping — any playback can loop endlessly, positioned or attached, private or worldwide. Perfect for ambience and background music.
-
Sound zones — spawn an invisible trigger sphere at any position and radius; every player standing inside hears its sound, players leaving stop hearing it. Parent the zone to a moving object and its audio rides along frame-by-frame — roaming ambience, event perimeters, hazard areas, moving machines. A
sourceOffsetplaces the audio point anywhere inside the zone. - Built for reuse — re-entering a zone reuses the same playback instead of stacking a second one; re-saving a file with identical content does not restart audio that is already playing. Idle emitters pool back per player, so repeated plays spawn no entity churn.
- Surgical stop control — stop by playback id (kills exactly one play invocation), by sound name (every instance, worldwide), by name-for-one-player, or wipe everything a player is currently hearing. Stopping by name works even after the file behind the sound was replaced or deleted.
-
Playback ids — every
Playreturns an id;IsSoundPlayingqueries whether it still produces sound,StopSoundtakes it down, and0means the play was rejected — so your plugin always knows what is on air. - Hot-reload safe — a plugin reload re-reads the folder, unregistering every sound that vanished from disk so nothing ghosts on. Reloaded versions of the same file swap into the registry cleanly.
- Robust by design — locked file mid-write? The watcher debounces writes and retries against transient IO locks. Watcher event buffer overflow? A full rescan re-converges the registry with the folder automatically. Corrupt or non-ogg bytes? Rejected with a logged reason before they can reach memory.
- Long sounds handled — segments longer than ~36 s are clamped by rewriting the ogg granule position, so the client reports and ends the sound exactly on schedule. Longer audio simply lives in segment folders.
- Invisible by nature — playback rides a vanilla client entity scaled down to nothing; players never see the machinery, they just hear the sound.
- Built-in admin tooling — a live sound table (names, CRCs, per-segment durations), instant in-game test playback, and multi-form stop commands.
-
Dev visualization — with the client's
debug.soundsflag on, a developer standing in a sound zone sees its sphere and labels drawn in world space. Tune placement without guessing.
How it works
SoundLibrary is fully server-side, and nothing it does touches the game's server entity list:
-
A cassette holds every loaded sound's
.oggbytes in server FileStorage under a fixed network id, so client-side caches stay warm across plugin reloads. - On play, an invisible speaker is streamed to the target client as pure packets — it never enters the server entity list, so the game cannot save it, cull it or destroy it behind the plugin's back.
- The speaker plays the audio through vanilla phone-network RPCs (the answering-machine path), the exact wire format the game itself uses.
-
Clients missing the bytes request them via
sv_requestfile; the cassette answers with token-bucket rate limiting that mirrors vanilla's request queue and honors your server'sfilerequestbytespersecond,filerequestbytesburstandfilerequestqueuelengthsettings.
The result: custom audio that behaves like official content, on stock clients, with no mods, no downloads and no compatibility patches.
API for developers
SoundLibrary is built ecosystem-first. Call it from any other plugin through Plugin.Call, or reference it directly for the full typed surface:
-
Playback —
PlaySoundAt(name, position, loop = false)— world-positioned playback for every connected player
PlaySoundFor(name, player, boneId = 0, loop = false)— attached to a player, heard only by that player
PlaySoundFor(name, entity, boneId = 0, loop = false)— attached to an entity, heard by its network subscribers
PlaySoundForSubscribers(name, entity, boneId, loop)— explicit subscriber playback forPlugin.Callcallers
PlaySoundFor(name, connection, loop = false)— targeted at one network connection -
Stop —
StopSound(playbackId)— kill one exact play invocation and all of its emitters
StopSound(name)— stop every playing instance of the sound, worldwide
StopSound(name, player)/StopSound(name, connection)— stop that sound for one listener only
StopSound(player)/StopSound(connection)— stop everything that listener currently hears -
Inspect —
IsSoundLoaded(name),GetLoadedSoundNames(),GetSoundDuration(name)(total across segments),GetSoundPath(name)(disk path of the first segment) -
Sound zones —
CreateSphere(position, radius = 5, soundName = null, loop = false, sourceOffset = default)spawns a trigger sphere and returns its GameObject for whatever your plugin does next: parent it to a moving entity, attach it to a base room, park it at an event spawn. Pass a null sound name to create a silent zone you fill in later;nullparent leaves the zone standalone. -
Runtime loading —
LoadSound(filePath)andUnloadSound(name)load or retire sounds without touching the folder. Paths outside the sound folder or beyond the first nesting level are rejected rather than half-loaded; unloading also stops any live playback of that sound.
Playback ids: every play method returns a nonzero id on success and 0 on rejection (unknown sound, dead parent, no connection). Keep the id to IsSoundPlaying(id) a specific invocation or StopSound(id) it down surgically — even while the same sound plays several times for several players.
For in-process plugins the typed surface goes deeper: SoundManager.CreateSound() opens a builder — WithPosition / WithParent(entity, bone) / WithLoop / For / ForPlayer / ForAllPlayers / ForSubscribersOf — finished with Play. Every internal detail is handled for you: dead parents abort cleanly instead of spawning orphaned audio, emitters pool per connection across plays, disconnected players free everything their playback held, and SoundData exposes name, path, CRC, byte count and duration of every segment for plugins that want to reason about audio, not just play it.
Performance
-
Throttled loading — the watcher loads at most 5 files per tick and hands off work from its background threads through a single main-thread drain, so a bulk drop of 400 fresh
.oggfiles cannot hitch the server frame. - Vanilla-shaped transfers — per-connection request budgets refill exactly like the game's own file-request queue, a full queue drops requests like vanilla does, and one connection's backlog can never starve another player's transfers.
- Memory that cleans up after itself — FileStorage entries are removed when the last sound using them unloads; identical files deliberately share one entry; disconnecting players detach their emitters and free their queues immediately.
- Pooled emitters — each player keeps a slot of idle emitters reused across playbacks, so repeated SFX never pile up network entities on their client.
Configuration — there is none
Say goodbye to config files. The folder is the configuration:
-
data/SoundLibrary/YourSound.ogg→ a sound namedYourSound -
data/SoundLibrary/YourSong/1.ogg,2.ogg,3.ogg, … → a sound namedYourSong, played as one ordered multi-part track. Segment files must be plain numbers; any other name is skipped with a logged reason.
That's the whole setup. No config JSON, no data file, no in-game editor to break, and nothing to migrate between versions. The folder lives in your server's data directory (carbon/data/SoundLibrary on Carbon).
Commands
-
snd.print— print a table of every loaded sound: name, CRCs, per-segment durations -
snd.play <sound> [player]— test-play a sound, attached to the given player (or the caller), and report the playback id -
snd.stop— stop everything the calling player hears -
snd.stop <name>— stop every instance of the named sound -
snd.stop <name> <player>— stop that sound for one player -
snd.stop <id>— stop playback #id (the number returned earlier by the play API)
The id form disambiguates itself: a live sound whose name happens to look numeric always wins over a playback id, so admin stops never misfire on oddly named audio.
Permissions
-
soundlibrary.admin— access to allsnd.*console commands. Grant to admins only; regular players have no sound visibility or control.
FAQ
How hard is setup?
Put a .ogg in the folder and it is playable the same second. One permission covers the admin commands. If you can copy a file, you have deployed audio.
Do my players need to install anything?
No. Sounds transfer over vanilla networking on demand; the client caches them like any official custom content. Players who already have the sound cached skip the transfer entirely.
How long can a sound be?
Any.
Can sounds move with objects?
Yes, twice over: parent playback to any entity (down to a bone) through the API, or parent a sound zone to a moving object and the zone carries its audio with it frame-by-frame. The zone does not care whether its parent is an entity or a plain GameObject.
Will it lag my server?
Loading is bounded per tick, playback rides pooled per-player emitters, transfers honor your existing vanilla request-rate settings, and nothing enters the server entity list. One huge folder loads at server pace, not at once.
What happens on a plugin reload?
The registry is rebuilt from the folder: sounds still on disk reload, sounds removed from disk vanish instead of playing as ghosts, clients with cached audio are not re-transferred, and live playback is stopped rather than left running on replaced audio.
Can several plugins play the same sound at once?
Yes — every play is an independent playback with its own id, its own target list and its own lifecycle. One plugin stopping its playback never touches another's.
Quick Start
- Make sure ServerPrefabs is loaded (SoundLibrary requires it).
-
Drop a
.oggfile intodata/SoundLibrary— it is loaded and playable this second. -
Grant yourself
soundlibrary.adminand runsnd.play YourSoundto hear it, thensnd.printto confirm the registry. -
Wire it into your plugins:
PlaySoundAt("YourSound", position)— that's the whole integration. - Update audio any time by replacing files in the folder; the watcher picks up the change while the server keeps running.
What servers build with it
- Event and raid drama — a map-wide klaxon the moment a raid event opens, with looping ambient score until it closes.
- Private cues — a chime only the rewarded player hears; voice lines only the quest owner hears.
- Living map ambience — looping shore, cave or machine sound zones parented to terrain features or moving objects.
- Minigame and event soundtracks — multi-part tracks long enough for real music, chained from segment folders.
- Plugin ecosystems — a sound layer your other plugins share, instead of every plugin reinventing audio.
Compatibility notes
- Requires the ServerPrefabs library plugin.
- Input format is .ogg (Vorbis) only — other formats and corrupt files are rejected with a logged reason.
- Sounds are heard on stock clients; nothing in this plugin asks a player to install mods, files or packs.
- Sound zones react to human players; NPCs are deliberately ignored, so NPC crowds never trigger zone audio.
Support
Discord: https://discord.gg/8nztXhvgDe — bug reports and API questions welcome. Author: 0xF.
