In short
For a teleport pad, put a Script in ServerScriptService that listens to the pad's Touched event, finds the player with Players:GetPlayerFromCharacter, and moves the whole character with character:PivotTo() to a spot just in front of the destination pad, facing away from it, with a cooldown of a couple of seconds per player so the destination pad does not send them straight back. To send players to another place in your game, call TeleportService:TeleportAsync(placeId, { player }) from a server Script inside pcall; it only works in the published game, not in Studio.
- Move the whole character with
PivotTo. SettingHumanoidRootPart.Positionmoves the root part away from the rest of the body, because Roblox updates the joints when Position is set. - A teleport menu should send the server a destination name, never a position. The server checks the name, the cooldown and any unlock rule before it moves anyone.
TeleportService:Teleport()is deprecated and client-side. UseTeleportAsyncon the server, wrap it inpcall, retry, and handleTeleportInitFailed.
On this page
Two kinds of teleport
A teleporter can mean three things in a Roblox game: a pad that sends you across the map, a menu of spots to jump to, or a portal into another level. The first two move a character inside the server the player is already in. The third sends the player to a different place in your game, which runs on its own servers. They use different APIs, so it helps to know which one you need.
| Inside the same server | To another place in your game | |
|---|---|---|
| The call | character:PivotTo(cframe) | TeleportService:TeleportAsync(placeId, players) |
| Used for | Teleport pads, a teleport menu, moving players from the lobby to an arena | Portals to another level, a lobby that sends players into a separate dungeon or match place |
| Runs in | A server Script | A server Script only. The client-side Teleport() is deprecated. |
| Works in a Studio playtest | Yes | No. Roblox's TeleportService does not support playtesting in Studio, so you test it in the published game. |
| What the player sees | A jump across the map, which can take up to 2 seconds if that area has to load first | Roblox's loading screen, then the other place |
| Coins, items and other data | Stay as they are | Roblox discards the player's local data, so anything that matters has to come from a data store in the other place |
This guide builds all three kinds in one server Script, so one set of rules covers every teleport in your game: the server decides, each player has their own cooldown, and the player's device only ever says which spot it would like.
| Piece | Where it lives | What it does |
|---|---|---|
TeleportPads Folder with PadA and PadB | Workspace | Two pads. Each pad's Destination attribute names the pad it sends players to. |
TeleportSpots Folder | Workspace | One part for each spot the menu lists. A WinsNeeded attribute locks a spot. |
PlacePortals Folder | Workspace | Portal parts. Each portal's PlaceId attribute is the place it sends players to. |
Teleporters Script | ServerScriptService | Runs all three kinds of teleport and creates the two remotes. The only place a teleport can happen. |
TeleportMenu LocalScript | StarterPlayer > StarterPlayerScripts | Draws the Teleport button and the list of spots, and sends the name of the spot the player picks. |
GetTeleportSpots RemoteFunction and TeleportRequest RemoteEvent | ReplicatedStorage (Teleporters creates them) | The first returns the list of spots to the menu. The second carries the picked name to the server. |
Workspace
TeleportPads Folder new
PadA Part new
PadB Part new
TeleportSpots Folder new
Spawn Part new
Lake Part new
Castle Part new
PlacePortals Folder new
DungeonPortal Part new
ServerScriptService
Teleporters Script new
StarterPlayer
StarterPlayerScripts
TeleportMenu LocalScript new
Step 1: Build two teleport pads
- In the Explorer, hover over Workspace, click the + button and insert a Folder. Name it
TeleportPads. The script looks for this exact name. - Insert two Parts into the folder and name them
PadAandPadB. Make each one a flat slab, for example 6 by 1 by 6 studs, and place them on the floor some distance apart. In the Properties window, tick Anchored, leave CanCollide and CanTouch on, and give them a bright colour and the Neon material so players can spot them. - Select PadA, scroll to the Attributes section of the Properties window and click +. Enter the name
Destination, choose the type String and click Save, then set its value toPadB. Do the same on PadB with the valuePadA. - Turn each pad so its front face points at open floor. Players arrive 3 studs in front of the destination pad's front face, facing away from it, so leave a few studs of open floor there. To see which face is the front, right-click the pad and choose Show Orientation Indicator: a blue line marks the front face. Turn the pad with the Rotate tool until that line points where players should walk off.
Players land beside the pad, not on it, on purpose. Roblox's debounce page notes that Touched fires many times in quick succession for one contact, so a player who arrived on top of the other pad would set it off again as soon as their cooldown ran out, and bounce back across the map.
You are not limited to two pads. A pad's Destination can name any part in TeleportPads, so three pads can send players round in a loop. For a one-way pad, point it at a part in TeleportPads that has no Destination of its own: that part receives players and sends nobody anywhere.
Step 2: Add the teleport script
Hover over ServerScriptService, insert a Script, name it Teleporters and paste the code below. It handles the pads now, and the menu and the portals from the next steps, so you add it once. The settings at the top are the lines you are meant to change.
-- Script in ServerScriptService named Teleporters.
-- Every teleport is decided here, on the server:
-- 1. Pads: touching a part in Workspace.TeleportPads takes you to the part named
-- in its Destination attribute.
-- 2. A menu: the TeleportMenu LocalScript asks for a spot in Workspace.TeleportSpots
-- by name. The server checks the name, the cooldown and the Wins the spot needs.
-- 3. Portals: touching a part in Workspace.PlacePortals sends you to the place
-- in its PlaceId attribute (another place in this game).
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local TeleportService = game:GetService("TeleportService")
local PAD_COOLDOWN_SECONDS = 2 -- pads ignore a player this long after any teleport
local MENU_COOLDOWN_SECONDS = 3
local PAD_ARRIVAL_GAP = 3 -- studs in front of a destination pad, so players land beside it
local PLACE_ATTEMPTS = 3
local padFolder = workspace:FindFirstChild("TeleportPads")
local spotFolder = workspace:FindFirstChild("TeleportSpots")
local portalFolder = workspace:FindFirstChild("PlacePortals")
-- This script makes both remotes itself, so do not add them by hand
local getSpots = Instance.new("RemoteFunction")
getSpots.Name = "GetTeleportSpots"
getSpots.Parent = ReplicatedStorage
local teleportRequest = Instance.new("RemoteEvent")
teleportRequest.Name = "TeleportRequest"
teleportRequest.Parent = ReplicatedStorage
local lastTeleport: { [Player]: number } = {}
local warnedPads: { [Instance]: boolean } = {}
local leavingForPlace: { [Player]: boolean } = {}
local function playerFromHit(otherPart: BasePart): Player?
local character = otherPart:FindFirstAncestorOfClass("Model")
return if character then Players:GetPlayerFromCharacter(character) else nil
end
local function onCooldown(player: Player, seconds: number): boolean
return os.clock() - (lastTeleport[player] or -math.huge) < seconds
end
-- How high a standing character's root part sits above the floor.
-- The formula is from Roblox's Humanoid.HipHeight reference (R6 rigs add the leg height).
local function rootHeight(character: Model, humanoid: Humanoid, rootPart: BasePart): number
local height = rootPart.Size.Y / 2 + humanoid.HipHeight
if humanoid.RigType == Enum.HumanoidRigType.R6 then
local leg = character:FindFirstChild("Left Leg")
if leg and leg:IsA("BasePart") then
height += leg.Size.Y
end
end
return height
end
local function teleportTo(player: Player, destination: BasePart, besidePad: boolean)
local character = player.Character
local humanoid = character and character:FindFirstChildOfClass("Humanoid")
local rootPart = humanoid and humanoid.RootPart
if not (character and humanoid and rootPart) or humanoid.Health <= 0 then
return
end
lastTeleport[player] = os.clock() -- set before waiting, so a second touch cannot start a second teleport
-- Face the way the destination's front face points (level, never tilted)
local facing = destination.CFrame.LookVector * Vector3.new(1, 0, 1)
if facing.Magnitude < 0.01 then
facing = Vector3.new(0, 0, -1)
end
facing = facing.Unit
-- Stand on its top face: a character's pivot is its root part, so lift it to standing height.
-- A pad puts players a few studs in front of itself instead, because a player standing
-- on a pad would set it off again with the next step.
local lift = destination.Size.Y / 2 + rootHeight(character, humanoid, rootPart)
local top = destination.Position + destination.CFrame.UpVector * lift
if besidePad then
top += facing * (destination.Size.Z / 2 + PAD_ARRIVAL_GAP)
end
local target = CFrame.lookAlong(top, facing)
-- With instance streaming on, load the area around the destination first (up to 2 seconds)
pcall(function()
player:RequestStreamAroundAsync(target.Position, 2)
end)
if player.Parent == nil or player.Character ~= character or humanoid.Health <= 0 then
return -- left, respawned or died while the area loaded
end
-- A seated character is welded to its seat, and moving it would bring the seat along.
-- Destroying the SeatWeld gets the character out of the seat first.
local seat = humanoid.SeatPart
if seat then
local seatWeld = seat:FindFirstChild("SeatWeld")
if seatWeld then
seatWeld:Destroy()
end
end
character:PivotTo(target)
-- Start the cooldown again on arrival: the pre-fetch above can take up to 2 seconds,
-- and the pad they land on must not send them straight back
lastTeleport[player] = os.clock()
player:SetAttribute("MovementCheckSkip", true) -- for a speed check script, if you have one
end
-- 1. Pads
if padFolder then
for _, pad in padFolder:GetChildren() do
if pad:IsA("BasePart") then
pad.Touched:Connect(function(otherPart: BasePart)
local player = playerFromHit(otherPart)
local destinationName = pad:GetAttribute("Destination")
if not player or typeof(destinationName) ~= "string" or onCooldown(player, PAD_COOLDOWN_SECONDS) then
return
end
local destination = padFolder:FindFirstChild(destinationName)
if destination and destination:IsA("BasePart") then
teleportTo(player, destination, true)
elseif not warnedPads[pad] then
warnedPads[pad] = true -- warn once, not on every touch
warn(`Teleporters: {pad.Name} points to {destinationName}, which is not a part in TeleportPads`)
end
end)
end
end
end
-- 2. The menu: the list of spots, then a request by name
local function winsOf(player: Player): number
local leaderstats = player:FindFirstChild("leaderstats")
local wins = leaderstats and leaderstats:FindFirstChild("Wins")
return if wins and wins:IsA("IntValue") then wins.Value else 0
end
local function winsNeeded(spot: Instance): number
local value = spot:GetAttribute("WinsNeeded")
return if typeof(value) == "number" then value else 0
end
getSpots.OnServerInvoke = function(_player: Player)
local list = {}
if spotFolder then
for _, spot in spotFolder:GetChildren() do
if spot:IsA("BasePart") then
table.insert(list, { name = spot.Name, winsNeeded = winsNeeded(spot) })
end
end
end
table.sort(list, function(a, b)
if a.winsNeeded ~= b.winsNeeded then
return a.winsNeeded < b.winsNeeded
end
return a.name < b.name -- same Wins: alphabetical, so the order never changes
end)
return list
end
teleportRequest.OnServerEvent:Connect(function(player: Player, spotName: unknown)
-- The client sends only a name. Never accept a position or a CFrame from a client.
if typeof(spotName) ~= "string" or #spotName > 50 or not spotFolder then
return
end
if onCooldown(player, MENU_COOLDOWN_SECONDS) then
return
end
local spot = spotFolder:FindFirstChild(spotName)
if not (spot and spot:IsA("BasePart")) then
return
end
if winsOf(player) < winsNeeded(spot) then
return -- locked for this player
end
teleportTo(player, spot, false)
end)
-- 3. Portals to another place in this game
local function sendToPlace(player: Player, placeId: number)
if leavingForPlace[player] or onCooldown(player, PAD_COOLDOWN_SECONDS) then
return -- already on the way, or touched a teleporter a moment ago
end
lastTeleport[player] = os.clock()
if RunService:IsStudio() then
print(`Teleporters: would send {player.Name} to place {placeId}. Place teleports only work in the published game.`)
return
end
leavingForPlace[player] = true
for attempt = 1, PLACE_ATTEMPTS do
local ok, err = pcall(function()
TeleportService:TeleportAsync(placeId, { player })
end)
if ok then
return
end
warn(`Teleporters: teleport attempt {attempt} for {player.Name} failed: {err}`)
task.wait(2)
end
leavingForPlace[player] = nil -- give up; touching the portal again tries again
end
-- A teleport can also fail after it starts. The player stays here and may try again.
TeleportService.TeleportInitFailed:Connect(function(player: Player, result: Enum.TeleportResult, message: string)
warn(`Teleporters: {player.Name} could not leave ({result.Name}): {message}`)
leavingForPlace[player] = nil
end)
if portalFolder then
for _, portal in portalFolder:GetChildren() do
if portal:IsA("BasePart") then
portal.Touched:Connect(function(otherPart: BasePart)
local player = playerFromHit(otherPart)
local placeId = portal:GetAttribute("PlaceId")
if player and typeof(placeId) == "number" and placeId > 0 then
sendToPlace(player, placeId)
end
end)
end
end
end
Players.PlayerRemoving:Connect(function(player: Player)
lastTeleport[player] = nil
leavingForPlace[player] = nil
end)
Teleporters creates the GetTeleportSpots RemoteFunction and the TeleportRequest RemoteEvent itself, so there is nothing to insert in ReplicatedStorage by hand. It connects the pads in TeleportPads and the portals in PlacePortals once, when the server starts, so pads and portals added while the game is running do nothing. A folder that is missing simply switches that kind of teleport off.
| Part | What it does | Why |
|---|---|---|
playerFromHit | Finds the character model the touching part belongs to and asks Players:GetPlayerFromCharacter whose it is. | A pad is touched by feet, hats and loose parts. Anything that is not part of a player's character gives nil and is ignored. |
onCooldown | Compares os.clock() with the time of the player's last teleport. | One cooldown per player, so a player standing near a pad never blocks anyone else. Nothing waits or sleeps. |
rootHeight | Works out how high a standing character's root part sits above the floor: half the root part's height plus the Humanoid's HipHeight, plus the leg length on R6 rigs. | This is the formula from Roblox's Humanoid.HipHeight reference. A character's pivot is its root part, so lifting the pivot by this much puts the feet on the surface for small, large and R6 avatars alike. |
The checks at the top of teleportTo | Returns unless the player has a living character with a root part, then records the time at once. | The streaming request below can make the script wait. Recording the time first means the many Touched events in that moment cannot start a second teleport. |
| The arrival point | The destination's top face, raised by rootHeight, facing the way its front face points with any tilt removed. For a pad, it also moves 3 studs past the pad's front face. | The character arrives upright, not inside the destination part. Beside a pad its feet start level with the pad's top face, so it drops the pad's thickness to the floor. CFrame.lookAlong builds a CFrame at a position, facing a direction. |
RequestStreamAroundAsync | Asks Roblox to send the area around the arrival point to that player first, waiting at most 2 seconds. | Instance streaming is on by default for new places, so a far part of the map may not be loaded on the player's device yet. Roblox recommends this server call before moving a character somewhere far, though it does not guarantee what arrives. |
| The second check | If the player left, or the character respawned or died during the wait, nothing moves. | The player may have reset or left the game while the area loaded. Roblox's own pre-fetch example also checks the character is still in the game before moving it. |
The SeatWeld lines | If the character is sitting, destroys the weld that holds it to the seat. | Roblox welds a sitting character to its seat. PivotTo moves parts by changing their CFrame, and Roblox's reference says setting a part's CFrame also moves the parts joined to it, so without this the seat, and anything welded to it, would arrive with the player. Destroying SeatWeld is how Roblox's Seat reference gets a character out of a seat from a script. |
character:PivotTo(target) | Moves the whole character, every limb and accessory together, to the arrival point. | Roblox calls PivotTo the primary function for moving models from scripts. |
The second lastTeleport line | Starts the cooldown again on arrival. | The streaming request can take up to 2 seconds, as long as the pad cooldown itself, so without this line a player could arrive with no cooldown left. |
MovementCheckSkip | Sets an attribute on the Player after each move. | A server-side speed check would see the jump as a cheat. The sprint guide's MovementCheck reads this attribute and starts measuring again from the new position. Without such a script, the attribute does nothing. |
| Setting | Value here | What it changes |
|---|---|---|
PAD_COOLDOWN_SECONDS | 2 | How long pads and portals ignore a player after any teleport. |
MENU_COOLDOWN_SECONDS | 3 | The shortest gap between two menu teleports for one player. Pads and the menu share one clock per player, so a menu request within 3 seconds of a pad teleport is ignored too. |
PAD_ARRIVAL_GAP | 3 | How many studs past a destination pad's front face players arrive, so they land beside the pad, not on it. |
PLACE_ATTEMPTS | 3 | How many times a portal tries TeleportAsync, 2 seconds apart, before it gives up. |
Press Play now and step on PadA: you appear in front of PadB. The menu and the portals need the next two steps, but the script already runs without them.
Step 4: Teleport to another place
A place is a separate world inside your game, with its own servers: a lobby and a dungeon, or a hub and a set of levels. Players move between places with TeleportService, and Roblox runs it only in the published game, so this step ends in the Roblox app rather than in Studio.
- If the game is not published yet, choose File > Publish. New games are open only to you, people you give Edit access and group members with the right roles until you release them, which is all you need to test teleports yourself.
- Make the second place: open a new place from any Studio template, build your dungeon, then choose File > Publish As, click your game's tile, select Add as a new place and click Create.
- Copy the new place's ID. Open the Asset Manager from the Window menu or the Home tab, go to Places In Experience, right-click the place and choose Copy Asset ID, which copies its place ID.
- Back in your first place, insert a Folder named
PlacePortalsinto Workspace, and inside it an anchored Part namedDungeonPortal. Add a Number attribute namedPlaceIdand paste the ID as its value. - Press Play and touch the portal. Output prints a line starting
Teleporters: would send: that is all a portal does in Studio. - Publish again with File > Publish, so the published game has the portal, and test it from the Roblox app as in Step 5.
Teleporters calls TeleportAsync inside pcall, up to 3 times with 2 seconds between attempts, because Roblox's teleport page says teleports can fail and throw an error, and recommends retrying. A teleport can also start and then fail at the last moment without an error, leaving the player where they were. Roblox fires TeleportInitFailed for that, and the script clears the player's "leaving" flag, so touching the portal again tries again.
To bring players back, do the same in the other place: put a copy of Teleporters in its ServerScriptService and add a portal whose PlaceId is your first place's ID, copied from the Asset Manager the same way.
Who can join your other places
Roblox's security docs warn that once a client is inside one place of your game, it can teleport to any other place in it, whatever route you intended, and that turning off a place's Direct Access only removes the Join button on the website. The setting that changes this is Access Control for Places, under Audience > Access Settings in the Creator Dashboard:
| Setting | Who can join a place that is not the start place |
|---|---|
| Fully open | Anyone, through teleports from any game, deep links, game invites and joining a friend. |
| Limited to same universe | Only players teleporting from within your games. Both client and server teleports are allowed. |
| Secure within universe only | Only players your server scripts teleport from within this game. |
If players should only reach a place through your portal, a dungeon for example, choose Secure within universe only. Every teleport in this guide starts on the server, so it keeps working. Roblox also says many games do not need secure teleports, so a game whose places are all open to everyone can leave it as it is.
What older teleporter tutorials get wrong
If you are comparing this guide with a video or a forum post, these are the differences that matter, with the reason each pattern is a problem.
| The tutorial | The problem | This guide |
|---|---|---|
Sets hit.Parent.HumanoidRootPart.Position = target.Position | Roblox's reference says setting Position updates the part's joints so it moves relative to the parts it is joined to: the root part moves without the body. | Moves the whole character with PivotTo. |
Sets HumanoidRootPart.CFrame = target.CFrame | The middle of the character lands at the middle of the target part, partly inside it, and takes on the part's tilt. | Arrives at standing height above the top face, beside a pad, kept level. |
Uses one debounce variable for the whole pad | While one player teleports, the pad ignores everyone else. With no debounce at all, a player who lands on the other pad is sent straight back. | One cooldown per player, recorded before anything waits, and players land beside the pad. |
Calls TeleportService:Teleport(placeId, player) from a LocalScript | Roblox marks Teleport() deprecated, says not to use it for new work, and allows TeleportAsync only from server scripts. | Calls TeleportAsync in the server script. |
No pcall, no retry | Roblox's teleport page says teleports can fail and throw an error, and can also fail after they start. | Up to 3 attempts in pcall, and TeleportInitFailed lets the player try again. |
| Gives up because "it does not work" in Studio | TeleportService does not support playtesting in Studio. | RunService:IsStudio() prints what the portal would have done instead. |
| A teleport GUI whose buttons send a position | A cheater can fire the same remote with any position. | The buttons send a spot name, and the server looks it up. |
| Coins passed along in teleport data | Roblox says teleport data is visible to the client and unencrypted. | Tells you to keep coins and Wins in a data store and load them in the other place (see Carry data to the other place). |
What players can and cannot fake
Assume a cheater controls everything on their own device: they can edit TeleportMenu, fire both remotes as often as they like with any arguments, and move their own character. Here is what that gets them with this teleporter.
| A cheater tries to | Result | Why |
|---|---|---|
| Send a position or a CFrame through the menu remote | Ignored | TeleportRequest reads one string, a spot name of up to 50 characters. Anything else is dropped. |
| Ask for Castle without 3 Wins | Refused | The server reads the Wins from its own copy of leaderstats. The grey button is only a hint. |
| Ask for a spot that does not exist | Ignored | The server looks the name up in TeleportSpots and does nothing if no part has that name. |
| Fire the menu remote over and over | At most one menu teleport every 3 seconds | The cooldown is kept on the server, per player. |
| Set off a pad without walking to it | They reach the partner pad, which they could walk to anyway | Roblox lists teleporting to any position and manipulating Touched events among the things a client can do with its own character. |
| Join another place of your game directly | Possible unless Access Control for Places is Secure within universe only | See Who can join your other places. |
| Move their own character anywhere, a locked spot included | Possible | Each device controls its own character's movement. A locked spot is a convenience, not a vault, so check on the server before giving out anything valuable there. |
The last row is the honest limit of any teleporter. For a competitive game, add a server-side movement check like the one in the sprint guide. Teleporters already sets the MovementCheckSkip attribute that check reads, so its own teleports are not flagged: Roblox's security docs say games with legitimate teleports need exactly this kind of exemption in their movement checks.
Step 5: Test it
- Press Play. Step on PadA: you appear just in front of PadB, facing away from it. Turn round and step straight back onto PadB: nothing happens, because pads ignore you for 2 seconds after you arrive. Step off, wait 2 seconds and walk onto PadB again: you appear in front of PadA.
- Wait a few seconds, then click Teleport at the left of the screen. The list shows Lake, Spawn and "Castle (needs 3 wins)" in grey. Pick Lake: you appear on it. Open the menu again and pick Castle: nothing happens, and Output stays quiet.
- Give yourself 3 Wins (this needs a leaderstats script with a Wins IntValue, such as the leaderboard guide's). Click the Client/Server toggle to switch to Server, find
Players, your name,leaderstats,Winsin the Explorer and set its Value to 3 in the Properties window. Switch back to Client and open the menu again: Castle is purple and takes you there. - Touch DungeonPortal. Output prints a line starting
Teleporters: would sendwith your name and the place ID. That is expected in Studio, and it prints at most once every 2 seconds however long you stand on the portal. - Choose Server & Clients in the test mode dropdown, set 2 clients and press Play. Both players can use the pads at the same time, and one player's cooldown never blocks the other.
- Stop the test, publish, and play the game from the Roblox app. Touch the portal: Roblox's loading screen appears and you arrive in the other place. If you stay where you are, open the Developer Console with F9, switch its log to the Server view, which only the game's owner and group members with edit permission can see, and look for the
Teleporters:warnings, which include the reason Roblox gave.
Keep the Output window open (Window menu, then Output) during the Studio steps. Teleporters prints nothing when it works, apart from the portal line in step 4, so any warning or red error is worth reading. See Common errors.
Extending it
Teleport a team or a party together
TeleportAsync takes a list of players, so one portal can send a whole group to the same place. Roblox's teleport page suggests starting from the player who touched the portal: player.Team and Team:GetPlayers() for a team, or SocialService:GetPlayersByPartyId() with player.PartyId for a party. Pass that list instead of { player }, and set the leavingForPlace flag for each player in it.
Send a group to its own server
By default, teleported players join a public server of the other place. To send a group to a new reserved server instead, for example for a match, create a TeleportOptions, set its ShouldReserveServer to true and pass it as the third argument: TeleportService:TeleportAsync(placeId, players, options). Roblox notes that failures involving reserved servers in particular benefit from retries, which the script already does.
Carry data to the other place
Teleporting discards any local data for that player. Roblox's teleport data, set with TeleportOptions:SetTeleportData() and read in the other place with player:GetJoinData(), is visible to the client and unencrypted, so use it only for harmless choices such as which map to load. Keep coins, Wins and items in a data store, as in the DataStore tutorial, and load them again when the player arrives in the other place.
A custom loading screen
While a place teleport loads, players see Roblox's standard loading screen. To show your own, call TeleportService:SetTeleportGui() on the client with a ScreenGui, for example one kept in ReplicatedStorage. The teleports in this guide start on the server, so the client has to call it ahead of time, for example as soon as the player joins. Roblox says the screen has to be set again before each teleport and is not used for a place in a different game. It also notes that scripts inside that ScreenGui do not run, so design it as a still screen.
Round teleports and checkpoints
The round system moves every player into the arena with the same kind of PivotTo call at the start of each round, and PivotTo or SpawnLocations in the obby guide compares this approach with Roblox's spawn points. If a spot should only open after a round, the round system already awards the Wins that WinsNeeded reads.
Common errors and fixes
Open the Output window (Window menu, then Output) before you press Play. Teleporters names the pad when a Destination points nowhere, so most problems show up there.
Infinite yield possible on 'ReplicatedStorage:WaitForChild("GetTeleportSpots")'
TeleportMenu is waiting for the remote that Teleporters creates, so Teleporters is not running. Check that it is a Script, not a LocalScript, directly inside ServerScriptService, that it is not disabled, and that Output shows no red error from it. The script errors guide explains this warning in detail.
Teleporters: PadA points to PadC, which is not a part in TeleportPads
The pad's Destination attribute names something that is not a part directly inside the TeleportPads folder. Check the spelling and capitals of the attribute value and of the destination pad's name. The warning appears once per pad, the first time a player touches it.
Stepping on a pad does nothing
Check that the folder is named exactly TeleportPads and sits directly in Workspace when the game starts, that the pad's Destination attribute is a String (a Number attribute is ignored), and that you waited 2 seconds since your last teleport. Also check the pad's CanTouch is on: Roblox says connecting Touched to a part with CanTouch off throws an error, which stops the script.
I land inside a wall or fall off the edge
Players arrive 3 studs in front of the destination pad's front face. Right-click the pad, choose Show Orientation Indicator, and turn the pad until the blue line points at open floor. Menu spots put players on top of the part, so keep each spot on a floor.
The Castle button stays grey after I earned 3 Wins
The list is drawn when the menu opens, so close the menu and open it again. If it is still grey, check that leaderstats has an IntValue named exactly Wins, and that the Wins changed on the server, not only in Client mode.
The portal does nothing in the published game
Open the Developer Console with F9, switch its log to the Server view (only the game's owner and group members with edit permission see it, because Teleporters is a server Script) and read the Teleporters: warnings, which include the reason Roblox gave. Check that the portal's PlaceId attribute is a Number holding the ID from the Asset Manager, and that you published the place again after adding the portal.
Build it with RoCode
If you would rather not wire this up by hand, RoCode can build it in the place you have open. RoCode is an independent AI agent for Roblox Studio, not made by Roblox: you describe the feature in its web chat, and its Studio plugin creates the parts and the scripts in your Explorer, so you are not copying code out of a chat window.
Add teleporters: pads in Workspace.TeleportPads that send players to the pad named in their Destination attribute with a 2 second cooldown, a Teleport menu for the spots in Workspace.TeleportSpots where Castle needs 3 Wins, checked on the server, and portals in Workspace.PlacePortals that send players to the place ID in their PlaceId attribute with retries.
- Can look through your place when a step needs it, with Search Game and Search Scripts, so it can build on items, remotes or save code you already have instead of adding a second system
- Creates the folders and the pad parts with Create Instance and sets their properties with Set Property, and writes Teleporters and TeleportMenu with Create Script
- Is told to compile-check each script it writes with Check Script, which compiles it in Studio without running it
- Sends its changes in batches, and each batch is an undo point in Studio's history
Place teleports only run in the published game, so test those in the Roblox app yourself. With plugin 3.9 or newer RoCode can run a short playtest and read the errors, but it does not move a character or click, so step on the pads and open the menu yourself. A teleporter is one system among many in a game; building a whole game with RoCode works one system per prompt like this. How RoCode connects to Studio.
Questions
How do I make a teleport pad in Roblox Studio?
Give two parts a Destination attribute naming each other, then let one server script move whoever touches a pad with character:PivotTo to a spot just in front of the other pad, with a short per-player cooldown. Step 1 and Step 2 build exactly that.
Why does my teleporter not work in Studio?
Pads and menus work in Studio. Teleports to another place do not: Roblox's TeleportService does not support playtesting in Studio, so publish the game and test in the Roblox app. The script in this guide prints what a portal would have done instead, so you can still check that it fires.
How do I teleport players to another place in my game?
Add the place with File > Publish As (Add as a new place), copy its place ID from the Asset Manager, and call TeleportService:TeleportAsync(placeId, { player }) from a server Script inside pcall, retrying if it fails. Step 4 walks through it.
How do I make a teleport GUI?
Build the buttons in a LocalScript and send the server only the name of the spot the player picked. The server looks the name up in a folder, checks the cooldown and any unlock rule, then moves the character. Never send a position from the client: a cheater can send any position they like.
Why does my character get stuck or fall apart after teleporting?
Setting the root part's Position moves only that part, not the body joined to it, and copying a pad's CFrame puts the middle of the character inside the pad. Use character:PivotTo with a point at standing height above the floor, worked out from the Humanoid's HipHeight.
Can I send coins or items with a teleport?
Not safely. Roblox says teleport data is visible to the client and unencrypted, so it is fine for something like a chosen map, not for coins. Save coins and items in a data store and load them in the other place.
Sources and further reading
- Roblox Creator Docs: Teleport between places. TeleportAsync, Studio playtests, Access Control for Places, teleport screens, teleport data and failed teleports
- Roblox Creator Docs: TeleportService. the deprecated Teleport, server-only TeleportAsync and TeleportInitFailed
- Roblox Creator Docs: PVInstance:PivotTo. the primary function for moving models from scripts
- Roblox Creator Docs: BasePart.Position. why setting Position on a joined part moves it relative to the others
- Roblox Creator Docs: Humanoid.HipHeight. how high a standing character's root part sits, for R15 and R6 rigs
- Roblox Creator Docs: Seat. the SeatWeld that holds a sitting character, and destroying it to stand them up
- Roblox Creator Docs: Player:RequestStreamAroundAsync. asking for an area before a character moves there, with no guarantee of what arrives
- Roblox Creator Docs: Streaming techniques and conversion. proactive streaming before a teleport
- Roblox Creator Docs: Access control and confidentiality. why clients can reach any place in a game unless teleports are secure
- Roblox Creator Docs: Network ownership, movement validation, and physics. what a client can do with its own character, and teleport exemptions in movement checks
- Roblox Creator Docs: Create and publish games and places. publishing, who can open a new game, and adding a place with Publish As
- Roblox Creator Docs: Asset Manager. copying a place ID
- Roblox Creator Docs: Developer Console. the Server view of the log, and who can see it
- Roblox Creator Docs: Debounce patterns. why Touched fires several times for one contact
How the code was checked: every script on this page passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 15 October 2026). A type check catches misspelt APIs and wrong types, not game logic, so playtest in Studio before you publish.