BOOM!Just like that, your experience now has a music player!
Gone are the days of ugly/clunky music players; now it integrates straight into the TopBar!
If you want to give it a try, I have included a demo place here:
I have also added it to GitHub for workflow convenience:
Happy to create some documentation on this in the future.
I hope this resource is helpful, please let me know your thoughts.
Hi,
There might be an issue where , depending on the song title number of characters, it does not show all of the music player buttons, like pause and track forward…
I have observed that it may scale and clip out some buttons, however, they are still accessible by scrolling to the right.
I did experiment with TopBarPlus API, and I am planning a fix for this.
I will update this thread, the related repository, and the demo place once the fix is published.
After looking into this, I realized you may be requiring TopBarBeats from StarterCharacterScripts or StarterGui. Doing so will cause TopBarBeats to duplicate, as they are re-executed on reset.
TopBarBeats, and it appears TopBarPlus, were designed to run inside of a persistent client context.
When re-required, even if duplication is prevented inside the module, the buttons inside of TopBarBeats cease to function as expected. Behavior is the same from TopBarPlus, as well.
If it is absolutely necessary to require TopBarBeats inside of StarterCharacterScripts or StarterGui, you may do so by caching TopBarPlus & TopBarBeats:
local TopBarPlus = require(ReplicatedStorage.Icon)
local TopBarBeats = require(ReplicatedStorage.TopBarBeats)
-- Cache it globally (not recommended but functional workaround)
TopBarPlus = _G.TopBarPlus or TopBarPlus
_G.TopBarPlus = TopBarPlus
TopBarBeats = _G.TopBarBeats or TopBarBeats
_G.TopBarBeats = TopBarBeats
Hopefully this addresses your issue, and if there are any features you would like to see in the future, please let me know!
Playlist loading no longer aborts on a single bad track.
Previously, if one track failed to load (invalid ID or name fetch failure), the entire loadTracks call would stop. Now it skips the bad track with a warning and continues loading
the rest.
Track switching now works correctly.
toggleMusic was calling Resume() even when switching to a different song, which wouldn’t actually start playback. It now detects new tracks and calls Play() instead.
Auto-advance properly restarts the next track.
When a song ended, the next track could start mid-way because TimePosition wasn’t being reset. Fixed.
Event connections are now properly cleaned up.
Ended, Paused, Resumed, and Played connections were never disconnected on destroy(), causing a memory leak. All connections are now tracked and disconnected during cleanup.
Removed the menu flicker workaround.
The nested task.delay deselect/reselect hack for rescaling the menu on long track names has been removed. Track title labels are now updated directly.
Fixed SoundGroup child lookup.
init() was checking for a child named “Sound” but the actual instance was named “TopBarTrack”, which could cause duplicate Sound instances.
Playlist order is now guaranteed.
loadTracks uses ipairs instead of pairs to preserve the order you pass tracks in.
Track index resets on reload.
Calling loadTracks again now properly resets CurrentTrackIndex to 1.
New Features
Volume Control (API)
Set and get playback volume programmatically:
TopBarBeats:setVolume(0.75) — accepts a value from 0 to 1.
TopBarBeats:getVolume() — returns the current volume.
Volume is applied at the SoundGroup level for cleaner audio mixing.
You can also pass volume in the config table: loadTracks({…}, { volume = 0.5 }).
Repeat Modes (API)
Control what happens when a track ends:
TopBarBeats:setRepeatMode(“All”) — loop the entire playlist (default, same as v1.x behavior).
TopBarBeats:setRepeatMode(“One”) — loop the current track.
TopBarBeats:setRepeatMode(“Off”) — play through the playlist once and stop.
Mute Support
TopBarBeats.IsMuted flag is available for muting without losing the volume setting.
Efficiency Improvements
Connection tracking via _connections table ensures zero leaked listeners.
destroy() now fully cleans up all state: connections, sound instances, UI elements, and the SoundGroup.
Removed unnecessary pcall wrapping around validateSoundId inside toggleMusic — validation already happens at load time. Kept as a safety net but failures are now handled
gracefully.
Eliminated redundant nil checks and tightened guard clauses throughout.
Usage Example
local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local TopBarPlus = require(ReplicatedStorage.Icon)
local TopBarBeats = require(ReplicatedStorage.TopBarBeats)