New: BloxMaps v4 is out: our biggest upgrade yet. Full Roblox games from one prompt, new Build A ___ Empire games, music in every game
Blog

How to Script in Roblox Studio as a Beginner

To script in Roblox Studio as a beginner, insert a Script into ServerScriptService, type a few lines of Luau such as print("Hello"), and press Play to see the result in the Output window. Then learn where each script type runs: Scripts run on the server, LocalScripts run on the player's device, and ModuleScripts hold shared code that you load with require().

Updated ยท by the BloxMaps team

Before you write any code, open three windows. Explorer is the tree of everything in your place. Properties shows the settings for whatever you select. Output shows prints and errors. If any of them are missing, turn them on from the View tab or the Window menu, depending on your Studio layout.

Keep Output open the whole time. It solves most beginner problems, because it tells you the script and the line that went wrong. Start from the Baseplate template so nothing else gets in the way.

What's the difference between a Script, a LocalScript and a ModuleScript?

This is where most beginners get stuck. A script with correct code can still do nothing if it's the wrong type or sits in the wrong place.

  • Script runs on the server. Use it for things every player must agree on, like giving coins, saving data, spawning items and dealing damage. Put it in ServerScriptService. A Script also runs inside a part in Workspace, which is handy for a part that reacts when touched.
  • LocalScript runs on one player's device. Use it for things only that player sees or does, like UI buttons, the camera and keyboard input. Put it in StarterPlayerScripts (inside StarterPlayer), StarterGui, StarterCharacterScripts or inside a Tool. A LocalScript in Workspace or ServerScriptService won't run, and Studio won't show an error about it.
  • ModuleScript doesn't run by itself. It holds code that other scripts load with require(). Put shared modules in ReplicatedStorage, which both the server and the client can reach. Put server-only modules in ServerScriptService or ServerStorage.

A simple rule: if it changes the game for everyone, it belongs on the server. If it's about what one player sees, it belongs in a LocalScript. Never let the client decide things like coin counts, because players can change what runs on their own device.

Scripts also have a RunContext property in Properties. Leave it on Legacy while you're learning, and the placement rules above apply.

To insert any of the three, hover over a folder in Explorer, click the + button and pick the type. To rename it, right-click it and choose Rename.

How do I write my first script?

Your first script adds a coin counter to the leaderboard for each player who joins. It uses the Players service and its PlayerAdded event.

Steps

  1. In Explorer, hover over ServerScriptService, click + and choose Script.
  2. Delete the print("Hello world!") line that Studio adds.
  3. Type this:
local Players = game:GetService("Players")

Players.PlayerAdded:Connect(function(player)
    local leaderstats = Instance.new("Folder")
    leaderstats.Name = "leaderstats"
    leaderstats.Parent = player

    local coins = Instance.new("IntValue")
    coins.Name = "Coins"
    coins.Value = 0
    coins.Parent = leaderstats

    print(player.Name .. " joined")
end)
  1. Press Play. When your character spawns, the leaderboard in the top right shows Coins at 0.
  2. Check Output. It shows your name followed by "joined".
  3. Press Stop before you edit anything. Changes you make during a test are lost when you stop.

Here's what each part does:

  • game:GetService("Players") gets the Players service. Always get services with GetService.
  • PlayerAdded:Connect(function(player) ... end) runs the function each time someone joins and passes that player in.
  • Instance.new("Folder") creates a new object. When a player has a folder named exactly leaderstats, Roblox shows it as the leaderboard. Each value inside it becomes a column.
  • Setting Parent last is a good habit. The object enters the game only after it's fully set up.

Watch the spelling. leaderstats must be all lowercase, or the leaderboard won't show.

How do I make a part do something when it's touched?

Now give players a way to earn coins. This script lives inside a part, so script.Parent is the part.

  1. Insert a Part from the Home tab. In Properties, tick Anchored so it doesn't fall. Turn CanCollide off if you want players to walk through it.
  2. Hover over the part in Explorer, click + and add a Script.
  3. Replace the default line with this:
local Players = game:GetService("Players")
local part = script.Parent
local cooldown = false

part.Touched:Connect(function(hit)
    local player = Players:GetPlayerFromCharacter(hit.Parent)
    if player and not cooldown then
        cooldown = true
        player.leaderstats.Coins.Value += 1
        task.wait(1)
        cooldown = false
    end
end)
  1. Press Play and walk into the part. Coins goes up by at most one per second.

Here's why each piece matters:

  • Touched fires for anything that touches the part. A single step can fire it many times, once for each body part. The cooldown variable is often called a debounce. It stops one touch from adding 20 coins.
  • hit is the body part that touched, such as LeftFoot. Its Parent is the character model. GetPlayerFromCharacter turns that model into a player. If something else touched the part, like a loose block, it returns nil, and the if player check skips it.
  • task.wait(1) pauses this function for one second without freezing the game.

All players share this one cooldown, so two players touching at once block each other. That's fine for learning. Later you can store a cooldown for each player in a table.

How do I share code with a ModuleScript?

A ModuleScript lets you write code once and use it from many scripts. In this example a LocalScript uses a small module, so you also see the client side.

Make the module

  1. Hover over ReplicatedStorage, click +, choose ModuleScript and rename it Messages.
  2. Studio fills it with local module = {} and return module. Replace all of it with this:
local Messages = {}

function Messages.welcome(name)
    return "Welcome, " .. name .. "!"
end

return Messages

The last line matters. Whatever a module returns is what require() gives back.

Use it from a LocalScript

  1. Expand StarterPlayer in Explorer. Hover over StarterPlayerScripts, click + and choose LocalScript.
  2. Replace the default line with this:
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Messages = require(ReplicatedStorage:WaitForChild("Messages"))
print(Messages.welcome(Players.LocalPlayer.Name))
  1. Press Play. Output prints "Welcome," followed by your name.

Notice two things here:

  • Players.LocalPlayer only works in LocalScripts. On the server there's no single local player, so it's nil.
  • WaitForChild waits until the object exists. You need it on the client because objects load over the network and may not be there when your script starts.

What do the errors in the Output window mean?

Errors show in red and warnings in orange. Each one names the script and the line number. Click the message in Output to jump straight to that line. These are the ones you'll see first.

Expected 'end' (to close 'function' at line 3)

The message ends with "eof", which means the end of the file. You opened a block and never closed it. Every function, if ... then, for and while needs a matching end. A function that starts with Connect(function(player) closes with end). The bracket closes Connect(. The Script Editor also underlines syntax errors in red before you press Play.

leaderstats is not a valid member of Player "Players.YourName"

You asked for a child that isn't there. Either the name is spelled differently, such as a capital L or an extra space, or your script ran before another script created the object. During Play, check the exact name in Explorer. If another script creates the object, use WaitForChild("leaderstats").

attempt to index nil with 'leaderstats'

You tried to read something from a value that was nil. In the touch script, this happens if you remove the if player check and something that isn't a player touches the part. Look at the variable before the dot and work out why it's empty.

Infinite yield possible on 'ReplicatedStorage:WaitForChild("Messages")'

This is a warning, not a crash. WaitForChild has waited a few seconds without finding the object. Almost always the name doesn't match. Fix the string or rename the object.

Module code did not return exactly one value

The ModuleScript is missing its return line, or it returns more than one thing. Make sure the very last line is a single return, such as return Messages.

Requested module experienced an error while loading

The ModuleScript itself hit an error while it ran, so require() failed. Scroll up in Output to the first red line. That's the real problem, and it points to a line inside the module.

Nothing happens and there's no error

Usually the script is the wrong type or in the wrong place, such as a LocalScript in Workspace or ServerScriptService. Add print("running") as the first line. If it never prints, the script isn't running at all.

What should I learn next?

With those three scripts working, you know the basic pattern: get a service, connect to an event, change something and check Output. Build on it in this order:

  1. RemoteEvents in ReplicatedStorage. They let a LocalScript, like a shop button, ask the server to do something. The LocalScript calls remote:FireServer(), and a Script listens with remote.OnServerEvent:Connect(function(player) ... end). Roblox passes in the player who fired it, and the server should always check that the request is allowed. The remote events docs walk through it.
  2. DataStoreService to save coins between sessions. To test it in Studio, publish the place and turn on Enable Studio Access to API Services under Game Settings, Security.
  3. UI built from ScreenGui, TextLabel and TextButton, driven by LocalScripts in StarterGui.
  4. MarketplaceService for game passes, once players have something worth buying.

Change one thing at a time and press Play after each change, so you always know which edit caused an error. The official scripting docs cover every service in depth. When you're ready to plan a full project, How to make a Roblox game covers the rest of the process.

Doing it faster with BloxMaps

BloxMaps won't teach you Luau, but it can give you a working project to study. You describe a game in up to 2,000 characters. BloxMaps builds the map, the UI, the shop and the gameplay scripts, then gives you one .rbxl file. Open it in Roblox Studio, find the scripts in Explorer, and read them, edit them and test your changes while you watch Output. You still own every step from there, including publishing. BloxMaps isn't made by or affiliated with Roblox. Building and previewing is free, with 3 previews a day on the Free plan, and you pay only to export. You can describe a game in the Game Maker.

Questions

Is Luau the same as Lua?

Luau is Roblox's scripting language. It grew out of Lua 5.1 and adds features such as optional type checking and compound operators like +=. Most beginner Lua tutorials still apply, but Roblox-specific parts like services and events come from the Roblox docs.

Do I have to pay to script in Roblox Studio?

No. Roblox Studio is free to download, and you can write and test scripts without paying anything.

Can I script Roblox games in Python or JavaScript?

No. Scripts in Roblox Studio use Luau. If you already know another language, the basics like variables, functions, loops and if statements carry over quickly.

Where do I put a script that controls a button on screen?

Use a LocalScript inside the ScreenGui in StarterGui. If the button should change something for everyone, such as buying an item, have the LocalScript fire a RemoteEvent with FireServer and let a Script in ServerScriptService handle it through OnServerEvent.

Why does my LocalScript do nothing?

It's probably somewhere LocalScripts don't run, such as Workspace or ServerScriptService. Move it to StarterPlayerScripts, StarterGui or StarterCharacterScripts. Then add a print line at the top to confirm it runs.

Why does my script work sometimes but error other times?

This is usually a timing problem. The script runs before the object it needs has loaded, especially on the client. Use WaitForChild for objects that are created or loaded after your script starts.

Make a Roblox game from one sentence

Describe your idea. Get the map, UI, shop and gameplay in one Roblox Studio file. Free to build and preview.

Make a gameFree to try