Been playing with a little multi-speaker audio setup at home where each ESP32-S3 board receives the same AirPlay 2 stream and plays just its own channel, no wires between them. Front pair works fine but after 15 to 20 minutes the surround speaker is noticeably behind the fronts. Restart everything and it lines up again, then slowly drifts. I assumed the shared clock from the stream would keep them locked, but aparently that is not the whole story. Is there something I should be adjusting on the boards themselves, like nudging the audio output clock to match the timestamps? I have not touched any of that yet, kind of hoping it is a config thing rather than something I have to code. Also not sure how much buffering I acutally need. I bumped the buffer up thinking more headroom means less drift, but it just made the delay before playback longer and the drift still showed up eventually. Wi-Fi at my place is decent, one router, nothing fancy. Anyone run into this kind of slow drift with independent receivers? Even a hint on wether to look at the timestamps or the output clock first would save me a lot of guessing.
Why do my ESP32 speakers drift out of sync after about 20 minutes?
Oct 25, 2026
Last reply: 23 hours ago
3 Replies
First reply
The slow drift you are running into is a known limitation when using independent hardware receivers relying solely on network timestamps for synchronization. Even if the source stream provides a common timing reference, each ESP32 has its own internal clock oscillator, wich…
Read the full replyThe slow drift you are running into is a known limitation when using independent hardware receivers relying solely on network timestamps for synchronization. Even if the source stream provides a common timing reference, each ESP32 has its own internal clock oscillator, wich naturally drifts slightly over time when running under varying thermal or load conditions. Eventually, those tiny deviations accumulate, causing the audio timing to seperate. While adjusting the internal output clock or tweaking the receiver's local clock correction logic is technically possable, it is ussually more effective to manage the timing discrepancy at the buffering stage. Buffering is less about 'headroom' and more about absorbing the variable latency introduced by the Wi-Fi network. A stable, slightly larger buffer usually wins over a minimally sized buffer because it smooth out the jitter that causes the initial phase shift, minimizing the drift rate. If you are finding that the drift is noticable only after 20 minutes, it points heavily toward clock drift rather than network jitter, which usually causes immediate hiccups. You should investigate the ability to synchronize the internal timers across the units, perhaps using a precise network time protocol or a similar mechanism, rather than just the AirPlay timestamps. This allows the receiver to maintain a stricter internal rhythm that is less susceptible to hardware-specific timing variations.
I ran into this exact clock stability problem once during a build. After troubleshooting the software and network side, I realized the root issue was power. The internal clock would drift because the voltage was fluctuating too much. It was a strange fix, but swapping the existing voltage regulator for a high quality LDO made all the difference. The timing suddenly became rock solid and didn't require any code changes to keep them aligned.
I wonder if you have considered meticulously checking the specific I2S peripheral configuration settings utilized by each unit, beyond just the general timing of the main clocks. The sample rate conversion logic within the peripheral handles timing offsets in a way that might diverge if the parameters are even slightly non-identical across the boards. Have you confirmed that the receiving code on all the individual boards is set to the precise same sample rate and word length for the incoming stream?
Join the Discussion
Have something to add? Share your thoughts — no account required.
Didn't find your answer?
Ask the community — no account required