How to create a Server Sided Music System

I have made a similar module in the past. It has all the features that you might need for controlling the music. I know that not all stuff is optimized, and I have used bad methods, so do take note that you should only use the code as an example rather than a finished product because this is not a finished product. It features playing, preloading, skipping, skipping to the previous song, autoplay, set timestamps, subtitles, etc.


If you are interested in the code, it’s going to be below. Also, I embedded the subtitle reader in my video player module. I actually code most of the stuff related to audio on my audio player, then embed the code into my other modules. I share this because I want to see other people get inspired and create more useful resources like this one. Also, this is a small addition to this tutorial for people seeking to implement other functions other than just playing and stopping; please only use this as an example and not a finished product. The code is not optimized well and can’t be scaled; I made too many functions when I could do it with fewer.


  • The whole code:

Disclaimer: the code below was meant to be ran on the client, if you wish to change it to the server you might have to do a little more manual edits.

local Tracks = 
	{
		[1] = {
			["Original"] = 13697265197,
			["Name"] = "Tomodachi no Kioku ver.b"
		},
		[2] = {
			["Original"] = 13697280194,
			["Name"] = "Modokashii Kimochi"
		},
		[3] = {
			["Original"] = 13697288644,
			["Name"] = "Hiruyasumi no Okujou"
		},
		[4] = {
			["Original"] = 13697299240,
			["Name"] = "Lunch for Two People"
		},
		[5] = {
			["Original"] = 13697306954,
			["Name"] = "My Precious Friend"
		},
		[6] = {
			["Original"] = 13697311245,
			["Name"] = "Fujimiya-san to Oshaberi"
		},
		[7] = {
			["Original"] = 13697350772,
			["Name"] = "Fujimiya-san ni Dokidoki"
		},
		[8] = {
			["Original"] = 13697369399,
			["Name"] = "Hase and Kiru"
		},
		[9] = {
			["Original"] = 13697394991,
			["Name"] = "After School Fun ver.a"
		},
		[10] = {
			["Original"] = 13697401626,
			["Name"] = "Gentle"
		},
		[11] = {
			["Original"] = 13697427739,
			["Name"] = "Atarashii Tomodachi"
		},
		[12] = {
			["Original"] = 13697433460,
			["Name"] = "Arm-in-Arm"
		},
		[13] = {
			["Original"] = 13697470886,
			["Name"] = "Don't Get Along So Well!"
		},
		[14] = {
			["Original"] = 13697474677,
			["Name"] = "Yakimoki Hase-kun"
		},
		[15] = {
			["Original"] = 13697480645,
			["Name"] = "After School Fun ver.b"
		},
		[16] = {
			["Original"] = 13697488249,
			["Name"] = "Gakkou no Kaerimichi"
		},
		[17] = {
			["Original"] = 13697496239,
			["Name"] = "Asa no Hikari no Naka de"
		},
		[18] = {
			["Original"] = 13697506835,
			["Name"] = "Fujimiya-san no Nikki"
		},
		[19] = {
			["Original"] = 13697512986,
			["Name"] = "Kienai Omoi, Itsumademo"
		},
		[20] = {
			["Original"] = 13697521310,
			["Name"] = "Fuan na Hibi"
		},
		[21] = {
			["Original"] = 13697526457,
			["Name"] = "Kako no Dekigoto"
		},
		[22] = {
			["Original"] = 13697533176,
			["Name"] = "Boukyaku no Kanata ni"
		},
		[23] = {
			["Original"] = 13697539583,
			["Name"] = "Monday"
		},
		[24] = {
			["Original"] = 13697559668,
			["Name"] = "Memory Loss"
		},
		[25] = {
			["Original"] = 13697567690,
			["Name"] = "False Memories"
		},
		[26] = {
			["Original"] = 13697571942,
			["Name"] = "Tomodachi ni Naritai"
		},
		[27] = {
			["Original"] = 13697576190,
			["Name"] = "Odayaka na Toki"
		},
		[28] = {
			["Original"] = 13697586283,
			["Name"] = "Emotions, Overflowing"
		},
		[29] = {
			["Original"] = 13697589994,
			["Name"] = "Tomodachi no Kioku ver.a"
		},
		[30] = {
			["Original"] = 13690557806,
			["Translated"] = {
				["Russian"] = 13728422986,
				["Subtitles"] = {
					["Russian"] = {
						{0, ""},
						{8, "Опять прийдёт новый рассвет - хочу встречать его с тобой,"},
						{15, "Напополам делить секунды, радость и печаль."},
						{22, "Бережно переверну страницу книги, где я воспоминания"},
						{32, "Собираю и храню."},
						{35.8, "Может быть, словами не выходит о чувствах рассказать ничего,"},
						{43, "Но, забыв про страх, попытаюсь сделать вперёд шаг!"},
						{50, "Протяни ладонь и в небо воспарим"},
						{54, "Безграничное и столь прекрасное."},
						{58, "По мосту из радуги отправимся"},
						{61, "В грядущий день, что с нетерпеньем ждёт."},
						{65, "Поборов сомнения, смогу найти"},
						{69, "То, чем дорожил давно когда-то ты,"},
						{72, "Улыбкою твоей согреюсь вновь и теперь хочу"},
						{78, "Любоваться ею всегда!"},
						{81, "Друзья на неделю"},
						{88, ""}
					}
				}
			},
			["Alternative"] = 13697594306, -- Piano
			["Subtitles"] = {
				{0, ""},
				{8, "I want to be with you when the new dawn comes again,"},
				{15, "I want to share the seconds, the joys and the sorrows."},
				{22, "I'll carefully turn the page of the book where I'll"},
				{32, "Collect and keep my memories."},
				{35.8, "Maybe words can't tell how I feel,"},
				{43, "But forgetting the fear, I'll try to take a step forward!"},
				{50, "Hold out your palm and you'll soar into the sky"},
				{54, "So boundless and so beautiful"},
				{58, "Over the bridge of the rainbow"},
				{61, "To the day to come That's waiting impatiently"},
				{65, "I'll overcome my doubts, I'll find"},
				{69, "What you once treasured"},
				{72, "I'll be warmed by your smile and now I want to"},
				{78, "Always be able to admire it!"},
				{81, "One Week Friends"},
				{88, ""}
			},
			["Name"] = "Niji no Kakera"
		},
		[31] = {
			["Original"] = 13697613040,
			["Subtitles"] = {},
			["Name"] = "Kanade"
		}
	}

local TweenService = game:GetService("TweenService")
local currentTrack
local currentVolume = 0.5
local isAutoPlay = false
local isSubtitles = false
local TracksList = {}

local TrackControl = {}
function TrackControl:Play()
	if not game.Workspace:FindFirstChild("Tracks") then
		local TracksFolder = Instance.new("Folder")
		TracksFolder.Name = "Tracks"
		TracksFolder.Parent = game.Workspace
		for _, v in Tracks do
			local Track = Instance.new("Sound")
			Track.Volume = 0
			Track.SoundId = "rbxassetid://".. v["Original"]
			Track.Name = v["Name"]
			Track.Parent = TracksFolder
			table.insert(TracksList, Track)
			local Equalizer = Instance.new("EqualizerSoundEffect")
			Equalizer.HighGain = 2
			Equalizer.MidGain = 2
			Equalizer.LowGain = 5
			Equalizer.Parent = Track
		end
		currentTrack = math.random(1, 29)
		game:GetService("ContentProvider"):PreloadAsync(TracksList)
	end
	
	TracksList[currentTrack]:Resume()
	TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.In), {Volume = currentVolume}):Play()
end

function TrackControl:Pause()
	if not game.Workspace:FindFirstChild("Tracks") then return end
	TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out), {Volume = 0}):Play()
	task.wait(0.5)
	TracksList[currentTrack]:Pause()
end

function TrackControl:Rewind()
	if not game.Workspace:FindFirstChild("Tracks") then return end	
	TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out), {Volume = 0}):Play()
	task.wait(0.5)
	TracksList[currentTrack]:Stop()
	if currentTrack - 1 > table.maxn(TracksList) then currentTrack = 32 else currentTrack -= 1 end
	TracksList[currentTrack]:Play()
	TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.In), {Volume = currentVolume}):Play()
end

function TrackControl:Skip()
	if not game.Workspace:FindFirstChild("Tracks") then return end	
	TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out), {Volume = 0}):Play()
	task.wait(0.5)
	TracksList[currentTrack]:Stop() 
	if currentTrack + 1 > table.maxn(TracksList) then currentTrack = 1 else currentTrack += 1 end
	TracksList[currentTrack]:Play()
	TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.In), {Volume = currentVolume}):Play()
end

function TrackControl:Stamp(newStamp)
	if not game.Workspace:FindFirstChild("Tracks") then return end	
	TracksList[currentTrack].TimePosition = newStamp
end

function TrackControl:Volume(newVolume)
	if not game.Workspace:FindFirstChild("Tracks") then return end
	currentVolume = newVolume
	TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.1, Enum.EasingStyle.Cubic, Enum.EasingDirection.InOut), {Volume = newVolume}):Play()
end

function TrackControl:ChangeTrack(newTrack)
	if not game.Workspace:FindFirstChild("Tracks") then return end
	TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out), {Volume = 0}):Play()
	task.wait(0.5)
	TracksList[currentTrack]:Stop()
	currentTrack = newTrack
	TracksList[currentTrack]:Play()
	TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.In), {Volume = currentVolume}):Play()
end

function TrackControl:GetTrack()
	if not game.Workspace:FindFirstChild("Tracks") then return end
	return TracksList[currentTrack]
end

function TrackControl:ChangeAutoPlay(newValue: boolean)
	if not game.Workspace:FindFirstChild("Tracks") then return end
	if newValue == true then
		isAutoPlay = true
		task.spawn(function()
			while task.wait() do
				if not isAutoPlay then break end
				if not TracksList[currentTrack].Playing then TrackControl:Skip() return end
				TracksList[currentTrack].Ended:Wait()
				TrackControl:Skip()
			end
		end)
	else
		isAutoPlay = false
	end
end

function TrackControl:ChangeToAlternative(newTrack)
	if not game.Workspace:FindFirstChild("Tracks") then return end
	if not TracksList[newTrack] then return end
	if not Tracks[newTrack]["Alternative"] then return end
	if newTrack == currentTrack then
		TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out), {Volume = 0}):Play()
		task.wait(0.5)
		TracksList[currentTrack]:Stop()
		currentTrack = newTrack
		TracksList[currentTrack].SoundId = "rbxassetid://".. Tracks[newTrack]["Alternative"]
		game:GetService("ContentProvider"):PreloadAsync({TracksList[currentTrack]})
		TracksList[currentTrack]:Play()
		TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.In), {Volume = currentVolume}):Play()
	else
		TracksList[newTrack].SoundId = "rbxassetid://".. Tracks[newTrack]["Alternative"]
		game:GetService("ContentProvider"):PreloadAsync({TracksList[newTrack]})
	end
end

function TrackControl:ChangeToLanguage(newTrack, newLanguage)
	if not game.Workspace:FindFirstChild("Tracks") then return end
	if not TracksList[newTrack] then return end
	if not Tracks[newTrack]["Translated"][newLanguage] then return end
	if newTrack == currentTrack then
		TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out), {Volume = 0}):Play()
		task.wait(0.5)
		TracksList[currentTrack]:Stop()
		currentTrack = newTrack
		TracksList[currentTrack].SoundId = "rbxassetid://".. Tracks[currentTrack]["Translated"][newLanguage]
		game:GetService("ContentProvider"):PreloadAsync({TracksList[currentTrack]})
		TracksList[currentTrack]:Play()
		TweenService:Create(TracksList[currentTrack], TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.In), {Volume = currentVolume}):Play()
	else
		TracksList[newTrack].SoundId = "rbxassetid://".. Tracks[newTrack]["Translated"][newLanguage]
		game:GetService("ContentProvider"):PreloadAsync({TracksList[newTrack]})
	end
end

function TrackControl:EnableSubtitles(newTrack, subtitleContainer, translatedLanguage)
	if not game.Workspace:FindFirstChild("Tracks") then return end
	if currentTrack == newTrack and TracksList[currentTrack].Playing == true then return end
	task.spawn(function()
		repeat task.wait() until currentTrack == newTrack and TracksList[currentTrack].Playing == true
		if Tracks[newTrack]["Translated"]["Subtitles"][translatedLanguage] then
			for i, v in Tracks[newTrack]["Translated"]["Subtitles"][translatedLanguage] do
				subtitleContainer.Text = v[2]
				if not Tracks[newTrack]["Translated"]["Subtitles"][translatedLanguage][i+1] then return end
				task.wait(Tracks[newTrack]["Translated"]["Subtitles"][translatedLanguage][i+1][1] - v[1])
			end
		else
			for i, v in Tracks[newTrack]["Subtitles"] do
				subtitleContainer.Text = v[2]
				if not Tracks[newTrack]["Subtitles"][i+1] then return end
				task.wait(Tracks[newTrack]["Subtitles"][i+1][1] - v[1])
			end
		end
	end)
end

return TrackControl