bark: keeps every room in sync over UDP multicast, without a clock server
live sync audio streaming for local networks
At a glance
- What is it?
- bark streams uncompressed 48kHz stereo audio over UDP multicast for a local network and keeps every receiver aligned using its own time synchronisation and latency detection, correcting drift with the Speex resampler instead of dropping buffers. It suits a house, a classroom or a rehearsal room with several receivers, and it is not a tool for the open internet.
- Who is it for?
- Adopt bark if you need several machines on one local network playing the same audio in step, and you are willing to tune the source delay until the receivers stop slewing. Do not adopt it across the open internet, since the transport is UDP multicast, or for a broadcast that must survive packet loss, since the format is uncompressed and the network is assumed to be reliable.
- Can I use it commercially?
- Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 163 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Multicast audio with the clock problem solved in the client
bark is described as low latency multi-receiver synchronised audio streaming for local networks, and the three design decisions in that sentence define the whole project.
The transport is UDP multicast carrying uncompressed 48kHz stereo audio. One source, many receivers, no per receiver stream, and the audio is not compressed, so there is no codec latency to tune and no CPU spent on encode and decode.
The clock is handled by bark itself. The built-in time synchronisation and latency detection means no high precision NTP is required, which is the part that separates this from a generic multicast audio sender. Ordinary NTP is accurate enough for a log file and not accurate enough for audio, and setting up a local time server for the sake of a music stream is a lot of ceremony.
The correction mechanism is the Speex resampler, which adjusts the audio playback rate to stay in sync. So the receivers do not drop or duplicate buffers to catch up. They play slightly faster or slightly slower, by a fraction of a percent, and glide into alignment. The design acknowledges that the network is not perfectly reliable and that clocks always run at slightly different rates, and it handles that with rate adjustment rather than with clicks.
The result is a tool aimed at a specific room: a house with speakers in two rooms, a classroom, a rehearsal space, a small venue, where a laptop or a Raspberry Pi plays the source and several other machines play the receivers.
The source side needs a virtual sink before it can stream
Running the server under Pipewire or Pulse is where the setup starts, and there is one dependency to get right first. If you are using Pipewire, you must have pipewire-alsa installed for this to work.
Then you create a virtual node for Bark to receive audio from, and you configure applications to send their audio to that node. With Pulse compatible tooling that is a null sink module load:
$ pactl load-module module-null-sink media.class=Audio/Duplex sink_name=Bark audio.position=FL,FRThe sink is created as a duplex, with the channel positions FL and FR, and the name Bark is what you will refer to it by.
Next you list the sources on the system to find the ID of the node you just created, following your sound server's ALSA device name schema:
$ pactl list sources shortThat listing shows one line per source with an ID, the name, the server and the format, and the Bark line in the example is id 145, float32le, 2ch, 48000Hz.
The server is then started with the multicast group and the device:
$ bark stream --multicast 224.100.100.100:1530 --device "pipewire:NODE=145"The address is a multicast group in the 224.100.100.100 range with a port, and the device string uses the source ID you just looked up. Applications that play into the Bark virtual node now feed the stream.
The receiver names its output sink and joins the same group
The receiver side is the mirror image, and it is a single command. You find the sink you want the receiver to output to:
$ pactl list sinks shortIn the documented example the Bark sink is id 145 and a USB audio interface output is id 3676, which is the one marked RUNNING. The Bark sink is listed as a sink as well as a source, because the duplex module gives it both.
You then start the receiver with the same multicast group and the output device:
$ bark receive --multicast 224.100.100.100:1530 --output-device "pipewire:NODE=3676"The important detail is that the receiver does not need to know the address of the source, only the multicast group. Everything else, the timing, the delay, the correction, is worked out from the packets themselves, which is what makes the setup simple enough to repeat on several machines.
One consequence of a duplex virtual node is that the same machine can be both a source and a receiver, and the listing format is what tells them apart, since the Bark node appears in both the sources and the sinks list with the same ID.
Configuration comes from three places, and the file is the weakest
Bark's options can be set on the command line, through environment variables, or in a configuration file, and the precedence is stated precisely. Options set in the configuration file take the lowest precedence, they are overridden by environment variables, and command line options take the highest precedence.
The file is named bark.toml, and Bark searches the XDG config directories for it while respecting any custom directories set in XDG_CONFIG_DIRS. By default it looks in $HOME/.config/bark.toml first, and then in /etc/bark.toml.
The file supports all the command line options Bark supports, so nothing is available on the command line that you cannot put in the file. The command line options and their corresponding environment variables are both shown in bark --help, which is also where you look up the environment variable name for a given flag.
The example in the documentation is short and shows the shape:
multicast = "224.100.100.100:1530"
[source]
device = "Bark"
delay_ms = 15
[receive.device]
name = "pipewire:NODE=MyNodeName"
period = 120 # default: send audio to hardware in discrete chunks of 120 frames
buffer = 240 # default: buffer 240 frames of decoded audio in memoryThe last two options are the ones that matter on the receiver. The period sets how many frames are handed to the hardware at a time, and the buffer sets how many frames of decoded audio are held in memory. Both are described as defaults, which means they are the values bark uses when you do not set them, and both are the knobs you turn when the receiver underruns or stutters.
bark stats is where you find out whether the sync is working
There is a third command, bark stats, which shows a live view of the state of all Bark receivers, and it is the instrument you tune with. Four timing fields are shown for each receiver, and each one isolates a different part of the problem.
Audio is the time offset of the audio stream, from when it should be according to the stream presentation timestamp to when the receiver is actually playing. A positive offset means the receiver is ahead of the stream and a negative offset means it is behind.
Buffer is the length of the audio data in the receiver buffer, which tells you how much runway the receiver has before it has to do something about a gap.
Network is the one-way packet delay between the stream source and the receiver, and the documentation is blunt about what you want: for best sync, this should be as stable as possible, not necessarily small.
Predict is the offset from the data timestamp in an audio packet, which is the stream source's time when the packet was sent, to what the receiver thinks that timestamp should be according to the measured clock difference and network latency. That is the number that says whether the receiver's model of the world is right.
The tuning guidance is practical. The stream source is responsible for setting the delay of the audio stream, and the delay wants to be as low as possible without causing receivers to slew or underrun their buffers too much. Receivers will always experience some slewing, because the network is not perfectly reliable and clocks always run at slightly different rates, and the aim is to keep that slewing to a minimum for the best quality. Keep an eye on bark stats while tuning the value.
The delay you set depends on your network and your sound cards
The tuning section is where the author explains why there is no single right value, and the reasoning is worth repeating before you spend an evening with bark stats.
The optimal delay depends on your network, particularly with respect to packet loss and latency stability, and on the latency introduced by sound cards. A receiver connecting wirelessly will need more delay to remain stable than one that is hard wired. That is the same point the stats view makes about the Network field: stability matters more than raw speed for keeping receivers aligned.
The sound card side is the part that surprises people. The author notes that a desktop with a USB DAC consistently tends to have less in its buffer than receivers with PCI DACs. In other words, the source machine and the receiver machines can have different timing behaviour, and a delay that suits one will not suit the other. If you see one receiver constantly ahead and another constantly behind, the difference is more likely in the audio hardware than in the network.
The last point is about expectations. The format is uncompressed 48kHz stereo, so the network has to carry a full quality stereo stream to every receiver, and a multicast group on a congested wireless segment will show up as jitter in the Network field long before it shows up as audible artefacts.
A workspace split into the protocol, the core and the binary
The repository is a Cargo workspace with three members, bark, bark-core and bark-protocol, on resolver version 2, with the two library crates referenced by path from the workspace dependencies. There are also a builder directory, a pkg directory for packaging, a script directory and a .cargo directory.
The dependency list is short and tells you what the implementation leans on. bitflags 2.6 with the bytemuck feature is used for bit level protocol work, bytemuck 1.18 with derive and must_cast for casting between byte layouts, derive_more 1.0 for the derive, display, from_str and deref implementations, heapless 0.8 for fixed capacity data structures, log 0.4 for logging, and thiserror 2.0 for error types. The presence of heapless and bytemuck in a protocol crate is the interesting part, since fixed capacity containers and explicit byte casting are what you reach for when a wire format has to be handled without dynamic allocation.
The licence is AGPL-3.0, and that is the one item to read before you use this on a network you do not control.
On maintenance, the two signals differ. The repository is not archived and the last push was on 2026-05-15, so work has continued through 2026. The release history, however, has a single entry, v0.6.0 from 2024-03-08. A project with commits and no releases for more than two years is a project you build from a branch, not from a tagged artefact, and the version number in your lock file will not tell you much about what you are running.
Editorial conclusion
Adopt bark if you need several machines on one local network playing the same audio in step, and you are willing to tune the source delay until the receivers stop slewing. Do not adopt it across the open internet, since the transport is UDP multicast, or for a broadcast that must survive packet loss, since the format is uncompressed and the network is assumed to be reliable. Verify first that your network carries multicast between the source and the receivers, that Pipewire installs include pipewire-alsa if you use Pipewire, and that the delay you set on the source matches what your sound cards can sustain, watching bark stats while you adjust it.
Frequently asked questions
What is bark and what does it stream?
bark is low latency multi-receiver synchronised audio streaming for local networks. It transmits uncompressed 48kHz stereo audio over UDP multicast, with built-in time synchronisation and latency detection so no high precision NTP is required, and it adjusts playback rate with the Speex resampler to stay in sync.
How do I start a bark stream and a bark receiver?
Create a virtual node with pactl load-module module-null-sink media.class=Audio/Duplex sink_name=Bark audio.position=FL,FR, then run bark stream --multicast 224.100.100.100:1530 --device "pipewire:NODE=145". On the receiving machine run bark receive --multicast 224.100.100.100:1530 --output-device "pipewire:NODE=3676" with the sink ID you found using pactl list sinks short.
Where does bark look for its configuration file?
Bark searches the XDG config directories for a bark.toml file, respecting custom directories set in XDG_CONFIG_DIRS, and by default looks in $HOME/.config/bark.toml first and then /etc/bark.toml. Configuration file options have the lowest precedence, environment variables override them, and command line options override everything.
What does bark stats show?
A live view of the state of all Bark receivers with four timing fields: Audio, the offset between the expected and actual playback time, Buffer, the length of the audio data in the receiver buffer, Network, the one-way packet delay between source and receiver, and Predict, the offset between the packet data timestamp and what the receiver expects it to be.
Does bark need Pipewire or Pulse, and what else?
It runs under Pipewire or Pulse, and if you use Pipewire you must have pipewire-alsa installed. The virtual node is created as a duplex null sink, and applications send their audio to that node so bark can capture it.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/haileys-bark)Was this useful?