§ How to · with Picoo

How to teleport players in Roblox

By Sametcan Tasgiran, Founder & Developer·Published ·Updated

Two completely different things are called teleporting in Roblox, and using the wrong one is why players end up stuck, split up, or missing their data.

Moving a character inside a place is one PivotTo call. Sending them to another place is a network call that can fail and needs handling. Picoo's teleport_system recipe ships both: two pads you can see in Edit mode and a cross-place helper.

Pads built in Edit mode

Workspace.TeleportPads holds two glowing 8 × 8 Neon pads you can see and move before pressing Play: SpawnToArena at the origin and ArenaToSpawn 120 studs away. Ask for your own pads (up to 24) with names, positions and destinations.

In-place teleport with PivotTo

Stepping on a pad moves the whole character with Model:PivotTo to that pad's destination. Dead characters and anything that is not a player are ignored.

Per-player cooldown

Two seconds per player by default (0.5 to 60 on request). Touched fires once per touching part, and each default destination sits on the other pad, so the cooldown is what stops players bouncing between them.

Cross-place helper

_G.TeleportServer.toPlace(player, placeId) sends one player to another published place with TeleportService:TeleportAsync inside pcall. It uses CROSS_PLACE_ID when no id is passed, and in Studio it warns and returns false instead of throwing.

Failure reporting

toPlace returns false and the reason when the call fails, so the calling script can tell the player, and TeleportInitFailed is connected to write the player, result and message to Output.

Arrival feedback

Both ends get a chime and a burst of particles in the pad's colour, and the arriving player gets a small camera shake, from Picoo's shared PicooFeedback module.

Files Picoo ships for this prompt

4 files · 1008 lines · one recipe call

ServerScriptService/TeleportServer.lua

Server Script: pad Touched handlers with PivotTo and the per-player cooldown, the destination table, and the toPlace cross-place helper.

143 lines

ReplicatedStorage/PicooFeedback.lua

Picoo's shared feedback ModuleScript (sounds, particles, camera shake). The teleport uses its pickup effect at both ends.

852 lines

StarterPlayer/StarterPlayerScripts/PicooFeedbackClient.lua

Loads the client half of PicooFeedback so camera shakes sent from the server play.

13 lines

Workspace/TeleportPads

Folder with the two Neon pads, each with a PointLight, built in Edit mode.

instance

Sample output: ServerScriptService/TeleportServer.lua

-- Touched fires once per touching PART, so a character stepping on a pad raises
-- it many times in a few frames. Without this the player teleports, lands on the
-- destination, and ping-pongs between the two pads indefinitely.
local lastUse: { [number]: number } = {}

local function canUse(player: Player): boolean
	local now = os.clock()
	if now - (lastUse[player.UserId] or -math.huge) < COOLDOWN then return false end
	lastUse[player.UserId] = now
	return true
end

local function moveCharacter(character: Model, target: Vector3)
	-- PivotTo moves the WHOLE model. Setting HumanoidRootPart.CFrame alone can
	-- leave accessories and limbs behind, or fight the physics solver mid-step.
	character:PivotTo(CFrame.new(target))
end

-- (excerpt) the pad Touched handlers sit here; then the cross-place helper:

--- Send a player to another PUBLISHED place. Returns false with a reason.
function TeleportServer.toPlace(player: Player, placeId: number?): (boolean, string?)
	local target = placeId or CROSS_PLACE_ID
	if target <= 0 then
		return false, "no place id configured — set CROSS_PLACE_ID to a published place"
	end

	-- Cross-place teleports DO NOT WORK IN STUDIO. Unprotected, this throws and
	-- takes the calling script with it — and it is the most common reason for a
	-- "teleport is broken" report that is not a bug at all.
	if RunService:IsStudio() then
		warn("[Teleport] TeleportService does not work in Studio — test in a published game")
		return false, "studio"
	end

	local ok, err = pcall(function()
		TeleportService:TeleportAsync(target, { player })
	end)
	if not ok then
		warn("[Teleport] failed for " .. player.Name .. ": " .. tostring(err))
		return false, tostring(err)
	end
	return true
end

How to teleport players in Roblox

"Teleport" covers two unrelated operations in Roblox, and most teleport bugs come from reaching for the wrong one.

Inside a place, teleporting is just moving a model: character:PivotTo(cf). It needs no service and no error handling, and it is what checkpoints, arenas, spawn pads and admin commands should use. The one detail is to move the whole character rather than only the HumanoidRootPart, and to land a little above the floor. Pads add a second detail: Touched fires once per touching part, so a pad that drops you onto another pad will bounce you back and forth unless each player gets a short cooldown.

Between places it is a request to Roblox's servers, and it fails for ordinary reasons: it never works in Studio, the destination must be a published place, a server can be full, a player can disconnect mid-call. Wrap it in pcall, listen for TeleportInitFailed, and tell the player something when it fails. A teleport that silently does nothing is indistinguishable from a broken button, and players retry it until they leave.

Picoo's teleport_system recipe covers both halves. It builds two glowing pads in Workspace.TeleportPads, visible in Edit mode, and a TeleportServer script that moves the whole character with PivotTo, gives each player a two-second cooldown and plays a chime and a particle burst at both ends. The same script exposes _G.TeleportServer.toPlace(player, placeId), which checks for Studio, calls TeleportAsync inside pcall and returns false with the reason when it fails.

Groups need one TeleportAsync call with the whole list. Teleporting players one by one hands each to the matchmaker separately, which is exactly how friends end up in different servers. Teleport data deserves suspicion: it travels through the player's client, so it is fine for context, such as which lobby or which difficulty, and wrong for anything that matters. Currency, inventory and progress come from your DataStore on arrival, every time.

See more on the Luau generator, the AI Luau script writer, the 3D model generator and the game builder, read how Picoo compares with ChatGPT, or browse the full blog.

Frequently asked

How do I teleport a player to a position in the same place?+

Call PivotTo on the character model, which moves the whole assembly. Setting HumanoidRootPart.CFrame directly is the older habit and can leave accessories behind or fight the physics solver. The recipe's pads do exactly this, to a destination a few studs above the floor so nobody lands inside it.

Why does my TeleportService call do nothing?+

Often because it ran in Studio: cross-place teleports only work in a published game, so test there. Live, teleports can still fail, for example when the destination server is full, the place is not published, or it belongs to another experience without third-party teleports allowed in Game Settings. Wrap the call in pcall and listen for TeleportInitFailed. The recipe's toPlace does both, and checks RunService:IsStudio() first so a Studio test warns instead of throwing.

Can I trust teleport data?+

Only as a hint. Teleport data travels through the player's client, so an exploiter can change it. Never restore currency, inventory or progress from it; read those from your DataStore on arrival. Use it for harmless context like which lobby they came from. To send some with toPlace, ask for TeleportOptions:SetTeleportData in a follow-up prompt.

How do I keep a group together?+

Send the whole group in one TeleportService:TeleportAsync call with every player in the list, so they are placed in the same server. Teleporting them one call at a time lets each land in a separately chosen server, and the older TeleportPartyAsync is deprecated. The recipe's toPlace sends one player; ask for a group version in a follow-up prompt.

What is a reserved server for?+

A private server instance nobody can join by chance, the standard way to run a match for a specific queue or party. Get an access code with TeleportService:ReserveServer, then teleport the group with TeleportOptions.ReservedServerAccessCode set to it. It is a follow-up prompt on top of the recipe's helper.

Related Picoo prompts