§ How to · with Picoo

How to make a lobby in Roblox Studio

By Sametcan Tasgiran, Founder & Developer·Published ·Updated

A lobby is not a room, it is a state machine wearing one — the geometry is the easy half.

Spawn, gather, count, launch. The part that breaks is almost always the launch: teleporting a group and keeping them together.

Spawn area

A themed room with SpawnLocation, so players land somewhere deliberate rather than on a grey baseplate.

Ready-up pad

Stand on the pad to join the queue; step off to leave. Server holds the roster.

Live counter

A billboard showing "3/8 ready" that updates as players come and go.

Countdown

Starts when the minimum is met, cancels if players drop below it.

Launch

TeleportService with a party so the queue arrives in the same server, not scattered.

Same-place mode

If you would rather not run two places, the same loop can just move players to an arena area instead.

Files Picoo ships for this prompt

3 files · 225 lines · ~35s · 2 credit

LobbyServer

Roster, countdown, minimum/maximum, teleport with party.

120 lines

LobbyMap (tree)

Spawn room, ready pad, counter billboard — visible in Edit mode.

60 lines

LobbyClient

Countdown UI and ready-state feedback.

45 lines

Sample output: ServerScriptService.LobbyServer

-- Teleport the READY GROUP together. TeleportPartyAsync keeps them in one
-- server; teleporting players one by one scatters them across new servers,
-- which is the classic "my friends ended up in different matches" bug.
local TeleportService = game:GetService("TeleportService")

local function launch(readyPlayers: { Player })
	if #readyPlayers < MIN_PLAYERS then return end

	local ok, err = pcall(function()
		TeleportService:TeleportPartyAsync(MATCH_PLACE_ID, readyPlayers)
	end)

	-- Teleports fail: place restrictions, rate limits, a player leaving
	-- mid-call. Failing silently leaves everyone standing on the pad
	-- wondering why nothing happened.
	if not ok then
		warn("[Lobby] teleport failed: " .. tostring(err))
		for _, plr in ipairs(readyPlayers) do
			stateEvent:FireClient(plr, { kind = "launch_failed" })
		end
	end
end

-- The roster is server-side. A client saying "I'm ready" is a request.
readyPad.Touched:Connect(function(hit)
	local plr = playerFromHit(hit)
	if plr and not ready[plr] then
		ready[plr] = true
		updateCounter()
	end
end)

Building a lobby in Roblox Studio

Lobbies look like a building task and behave like a state machine. There is a room, yes, but the work is in the states: waiting, enough players, counting down, launching — and every transition has a failure case that shows up as "the game just didn't start".

The roster belongs on the server. It is tempting to count characters standing near the pad on each client, and it will disagree between players the moment someone lags. Keep one list on the server, update it on touch and on PlayerRemoving, and push the count out. The billboard then shows the same number to everyone because there is only one number.

Launching is where most lobbies break. Teleporting players one at a time hands each of them to the matchmaker separately, and they land in different servers — this is the "my friends ended up in another match" report. TeleportPartyAsync moves the group as a unit. And teleports genuinely fail: place restrictions, rate limits, a player disconnecting mid-call. A pcall around it plus a visible message beats everyone standing on a pad wondering whether they pressed something wrong.

The last decision is whether the match is a separate place at all. A separate place gives every round a clean world, which matters if your game leaves debris — spawned NPCs, dropped items, terrain edits. If the match is just an arena on the far side of the map, skip teleporting entirely: move the players, reset the arena, and you have removed a whole class of failure from your game.

See more on the Luau generator, the game builder, or browse the full blog.

Frequently asked

Should the lobby be a separate place or the same one?+

Separate if matches need a clean world each round — a fresh server per match means no leftover state, no parts from the last game, no memory creep. Same place is simpler and fine when the "match" is just an area you walk into, and it avoids teleport failures entirely.

Why do my friends end up in different servers?+

Because each player was teleported individually. TeleportAsync per player sends each into whatever server the matchmaker picks. Use TeleportPartyAsync with the whole group so they are placed together.

How do I show how many players are waiting?+

Keep the roster on the server and push the count to clients when it changes — not a client-side count of characters near the pad, which disagrees between players and can be spoofed.

What happens if a player leaves during the countdown?+

Re-check the minimum on PlayerRemoving and cancel if you drop below it, otherwise you teleport a party of two into an eight-player match. That check is the difference between a lobby that feels solid and one that feels random.

Can players join a match in progress?+

That is a different system — reserved servers plus a join code, or letting late arrivals spectate. A queue lobby assumes everyone starts together; mixing the two is where lobby code usually gets messy.

Related Picoo prompts