Tutorial · Gameplay

How to make a tool in Roblox Studio

Anything a Roblox player carries in their hotbar and holds in their hand is a Tool: swords, potions, flashlights, building tools, fishing rods. A tool is a container with a part named Handle for the hand to hold and a script that runs when the player clicks. This guide builds one from scratch, explains how it is held, then makes a healing potion with a limited number of uses and a flashlight, and shows how to hand tools out from the server and keep them after a player dies.

Updated 5 November 20267 min readBeginnerLuauWritten by

In short

Insert a Tool into StarterPack, put a Part named Handle inside it (not anchored, not colliding), and add a Script inside the Tool that connects tool.Activated. Activated fires when the player clicks or taps while holding it, and a server Script inside the tool receives it, so the effect counts for everyone. Use Equipped and Unequipped for things that happen when it is taken out or put away.

  • The part must be named exactly Handle, or set the Tool's RequiresHandle off for a tool with nothing in the hand.
  • Never anchor the Handle: it pins the player in place.
  • Keep cooldowns and uses on the server script, not in a LocalScript.

Get the script

Healing potion tool script with uses and cooldown

A server Script inside a Tool that heals the holder on click, with a cooldown and a number of uses, and removes the tool when it is empty.

Script in the Potion Tool named PotionScript
-- Script inside the Potion Tool, named PotionScript.
-- Heals the holder on click, with a cooldown and a limited number of uses.
local tool = script.Parent :: Tool

local HEAL_AMOUNT = 40
local COOLDOWN_SECONDS = 3

local lastUse = 0

tool.Activated:Connect(function()
	local now = os.clock()
	if now - lastUse < COOLDOWN_SECONDS then
		return
	end
	-- the tool is inside the character while it is held
	local character = tool.Parent
	local humanoid = character and character:FindFirstChildOfClass("Humanoid")
	if not humanoid or humanoid.Health <= 0 or humanoid.Health >= humanoid.MaxHealth then
		return -- nobody to heal, or already at full health: do not waste a use
	end
	local uses = tool:GetAttribute("Uses")
	if typeof(uses) ~= "number" or uses <= 0 then
		return
	end
	lastUse = now
	humanoid.Health = math.min(humanoid.Health + HEAL_AMOUNT, humanoid.MaxHealth)
	tool:SetAttribute("Uses", uses - 1)
	tool.ToolTip = (uses - 1) .. " drinks left"
	if uses - 1 <= 0 then
		tool:Destroy()
	end
end)

RoCode builds it in the place you have open in Roblox Studio, on Windows or macOS with a desktop browser; on a phone the button sends the prompt and the link to your computer. Free to start, with free credits every day. More Roblox Studio scripts

Where to put it

Explorer: the Tool, its Handle and the Script
StarterPack
  Potion Tool new
    Handle Part new
    PotionScript Script new

Settings you can change

HEAL_AMOUNTIn the script
Health restored per drink. Default: 40.
COOLDOWN_SECONDSIn the script
Seconds before it can be used again. Default: 3.
UsesAttribute
Drinks left. Set a number attribute named Uses on the Tool; the tool is removed at 0. Default: 3.

What a cheater can and cannot doThe cooldown and the uses are kept by the server script, so clicking faster or firing the tool's activation from a cheat never heals more.

Type-checked with luau-lsp strict against Roblox's API definitions on 5 November 2026. Not playtested in Studio yet: press Play and watch the Output window before you publish.

Prompt for RoCode

Make a healing potion Tool in StarterPack: a Handle part, and a server Script that heals 40 health on click with a 3 second cooldown, has 3 uses stored in a Uses attribute, and destroys the tool when it runs out.

On this page
  1. Get the script
  2. How it works
  3. 1. Build it
  4. 2. Potion script
  5. 3. Flashlight
  6. 4. Give tools
  7. 5. Use animation
  8. Common errors
  9. With RoCode
  10. Questions
  11. Sources

How tools work

Everything in StarterPack is copied into each player's Backpack every time they spawn, and the Backpack is what the hotbar shows. When the player equips a tool (presses its number or taps it), Roblox moves it from the Backpack into their character and welds its Handle to the right hand. Unequipping moves it back.

Tool events and properties you use most
NameWhat it is
ActivatedThe player clicked or tapped while holding it. Fires on the server and the client.
DeactivatedThey let go of the click.
Equipped / UnequippedTaken out or put away.
EnabledWhile false, clicks do not fire Activated.
RequiresHandleOff for a tool with no part in the hand, such as a spell.
CanBeDroppedWhether Backspace drops it on the ground.
GripWhere and at what angle the hand holds the Handle.
TextureId and ToolTipThe hotbar icon and the text when hovering it.

Step 1: Build the tool

  1. In the Explorer, hover over StarterPack, click + and insert a Tool. Name it Potion.
  2. Insert a Part into the Tool and rename it exactly Handle. Make it small, about 0.8, 1.2, 0.8. Leave Anchored off and untick CanCollide.
  3. For more detail, add more parts inside the Tool and join each to the Handle with a WeldConstraint (Part0 the Handle, Part1 the extra part). Unanchored, unwelded parts fall off.
  4. Press Play, press 1 to equip it and look at the hand. If it sits wrong, stop and change the Tool's Grip (or GripPos to move it, GripForward and GripUp to turn it), then try again.

For a hotbar icon, upload an image and paste its ID into TextureId; otherwise the hotbar shows the tool's name. ToolTip is the hint shown when hovering the slot.

Step 2: A healing potion with uses

Select the Tool and add a number attribute named Uses set to 3 (Properties window, Attributes, +). Then insert a Script into the Tool named PotionScript:

Script in the Potion Tool named PotionScript
-- Script inside the Potion Tool, named PotionScript.
-- Heals the holder on click, with a cooldown and a limited number of uses.
local tool = script.Parent :: Tool

local HEAL_AMOUNT = 40
local COOLDOWN_SECONDS = 3

local lastUse = 0

tool.Activated:Connect(function()
	local now = os.clock()
	if now - lastUse < COOLDOWN_SECONDS then
		return
	end
	-- the tool is inside the character while it is held
	local character = tool.Parent
	local humanoid = character and character:FindFirstChildOfClass("Humanoid")
	if not humanoid or humanoid.Health <= 0 or humanoid.Health >= humanoid.MaxHealth then
		return -- nobody to heal, or already at full health: do not waste a use
	end
	local uses = tool:GetAttribute("Uses")
	if typeof(uses) ~= "number" or uses <= 0 then
		return
	end
	lastUse = now
	humanoid.Health = math.min(humanoid.Health + HEAL_AMOUNT, humanoid.MaxHealth)
	tool:SetAttribute("Uses", uses - 1)
	tool.ToolTip = (uses - 1) .. " drinks left"
	if uses - 1 <= 0 then
		tool:Destroy()
	end
end)

This is a server Script, so the heal and the use count are decided by the server: a cheater can fire the activation as fast as they like and still only heal once every 3 seconds. Because a fresh copy comes from StarterPack on every spawn, the player gets a full potion each life. To make it last across deaths, give it from the server instead, as in Step 4.

Step 3: A flashlight that turns on and off

A tool that toggles something is the same pattern with a state. Make a Tool named Flashlight with a Handle, put a SpotLight inside the Handle (untick its Enabled), and add this Script to the Tool:

Script in the Flashlight Tool named FlashlightScript
-- Script inside the Flashlight Tool, named FlashlightScript.
local tool = script.Parent :: Tool
local handle = tool:WaitForChild("Handle") :: BasePart
local light = handle:WaitForChild("SpotLight") :: SpotLight

light.Enabled = false

tool.Activated:Connect(function()
	light.Enabled = not light.Enabled
end)

tool.Unequipped:Connect(function()
	light.Enabled = false -- off when put away
end)

Turn the SpotLight so it points the same way as the hand by changing its Face property, and raise Range and Brightness for a horror map. Every player sees each other's flashlights, because the server switches them.

Step 4: Give tools from the server

StarterPack gives a tool to everyone. For tools earned or bought, keep the original in ServerStorage, where players cannot reach it, and clone it into the player's Backpack from a server Script. Putting a second copy in StarterGear means they keep it after dying, because Roblox refills the Backpack from StarterPack and StarterGear on each spawn.

Script in ServerScriptService named GiveTools
-- Script in ServerScriptService named GiveTools.
-- Gives a tool from ServerStorage.Tools to players who touch the GiveFlashlight part.
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")

local toolsFolder = ServerStorage:WaitForChild("Tools")
local pad = workspace:WaitForChild("GiveFlashlight") :: BasePart

local function giveTool(player: Player, toolName: string, keepAfterDeath: boolean)
	local template = toolsFolder:FindFirstChild(toolName)
	if not (template and template:IsA("Tool")) then
		warn("[GiveTools] no Tool named " .. toolName .. " in ServerStorage.Tools")
		return
	end
	local backpack = player:FindFirstChildOfClass("Backpack")
	local character = player.Character
	local alreadyHas = (backpack and backpack:FindFirstChild(toolName)) or (character and character:FindFirstChild(toolName))
	if alreadyHas or not backpack then
		return
	end
	template:Clone().Parent = backpack
	local starterGear = player:FindFirstChild("StarterGear")
	if keepAfterDeath and starterGear and not starterGear:FindFirstChild(toolName) then
		template:Clone().Parent = starterGear
	end
end

pad.Touched:Connect(function(hit: BasePart)
	local character = hit.Parent
	local player = if character and character:IsA("Model") then Players:GetPlayerFromCharacter(character) else nil
	if player then
		giveTool(player, "Flashlight", true)
	end
end)

Create a Folder named Tools in ServerStorage and move the Flashlight tool into it, then add a part named GiveFlashlight in Workspace. The check for an existing copy (in the Backpack, or in the character while held) stops one touch from filling the hotbar. The shop GUI guide uses the same giving function after a purchase, and the inventory guide manages many tools at once.

Step 5: Play an animation when used

A drink or a swing animation makes a tool feel finished. Animations on a player's own character are best started on their device, and they replicate to everyone. Add a LocalScript to the Tool:

LocalScript in the Potion Tool named PotionAnimation
-- LocalScript inside the Potion Tool, named PotionAnimation.
local tool = script.Parent :: Tool

local drinkAnimation = Instance.new("Animation")
drinkAnimation.AnimationId = "rbxassetid://0" -- your own animation ID

local track: AnimationTrack? = nil

tool.Equipped:Connect(function()
	local character = tool.Parent
	local humanoid = character and character:FindFirstChildOfClass("Humanoid")
	local animator = humanoid and humanoid:FindFirstChildOfClass("Animator")
	if animator and drinkAnimation.AnimationId ~= "rbxassetid://0" then
		track = animator:LoadAnimation(drinkAnimation)
	end
end)

tool.Activated:Connect(function()
	if track then
		track:Play()
	end
end)

tool.Unequipped:Connect(function()
	if track then
		track:Stop()
	end
	track = nil
end)

Make the animation in Studio's Animation Editor, publish it, and paste its ID. It must belong to you, or to your group for a group game; see Failed to load animation with sanitized ID if it does not play. The animation script guide covers priorities and looping.

Common errors and fixes

Open the Output window (Window menu, then Output) before you press Play.

The tool does not appear in my hand

The part is not named exactly Handle, or it is not a direct child of the Tool. If the tool should have nothing in the hand, untick RequiresHandle.

I cannot move while holding it

The Handle or another part of the tool is anchored. Untick Anchored on every part inside the tool.

Parts of the tool fall off

Extra parts are not welded to the Handle. Add a WeldConstraint from the Handle to each one.

Clicking does nothing

Check the Script is inside the Tool and is a Script, the Tool's Enabled is ticked, and ManualActivationOnly is off. Add print("activated") as the first line of the Activated handler.

Build it with RoCode

If you would rather not build tools by hand, RoCode can build them in the place you have open. RoCode is an independent AI agent for Roblox Studio, not made by Roblox: you describe the change in a web chat, and its Studio plugin creates the tool, its parts and its scripts in your Explorer.

You type

Make a flashlight Tool in ServerStorage.Tools with a handle and a spot light that toggles on click and turns off when put away. Give it, kept after death, to players who touch the part named GiveFlashlight, checked on the server.

RoCode does
  • Can look through your place first with Search Game, so it puts tools where your existing ones live
  • Creates the Tool, its Handle and the light with Create Instance, and writes the scripts with Create Script
  • Is told to compile-check each script it writes with Check Script
  • Sets an undo point before and after each batch of changes, so Ctrl+Z steps back one batch at a time

Equip the tool yourself after it is built and check how it sits in the hand; adjust Grip if needed. With plugin 3.9.15 or newer RoCode can control a test character, but it checks only what it chose to test. How RoCode connects to Studio.

Questions

How do you make a tool in Roblox Studio?

Insert a Tool in StarterPack, add a part named Handle inside it (unanchored), and a Script inside the Tool that connects tool.Activated. Step 1 and Step 2 walk through it.

Should a tool script be a Script or a LocalScript?

A Script for anything that changes the game, such as healing or damage, because Activated fires on the server too. A LocalScript for things only the holder needs: aiming, animations, the mouse.

How do I give a player a tool with a script?

Clone it from ServerStorage into player.Backpack on the server. Also clone it into player.StarterGear if they should keep it after dying.

How do I make a tool with no handle?

Untick the Tool's RequiresHandle. Activated still fires when the player clicks while it is selected.

How do I stop players dropping a tool?

Untick CanBeDropped on the Tool. While it is ticked, Backspace drops the equipped tool into Workspace.

Sources and further reading

How the code was checked: every script on this page passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 5 November 2026). A type check catches misspelt APIs and wrong types, not game logic, so playtest in Studio before you publish.

Try RoCode on your own place.

Free every day, no card required. Install the plugin, describe the feature, and review what lands in Studio.