In short
Put a LocalScript in StarterPlayer > StarterCharacterScripts. In it, find the character's Humanoid and its Animator, create an Animation with your AnimationId, load it with animator:LoadAnimation(animation), and call :Play() on the track it returns when UserInputService.InputBegan reports your key. An animation a player plays on their own character this way is seen by everyone in the server.
- The animation has to be owned by whoever owns the game (you or your group) or by Roblox. An animation someone else uploaded does not play in your game.
- Set the track's
PrioritytoAction, or the walk and idle animations can cover it. - Load animations with
Animator:LoadAnimation.Humanoid:LoadAnimationis deprecated.
Get the script
Animation on a key press
One LocalScript plays an animation on your character when you press E, with a cooldown, and every player in the server sees it.
-- LocalScript in StarterPlayer > StarterCharacterScripts named EmoteOnKey.
-- Press E to play an animation on your own character. Everyone in the server sees it.
local UserInputService = game:GetService("UserInputService")
local ANIMATION_ID = "rbxassetid://507770239" -- Roblox's own R15 wave; put your animation's ID here
local KEY = Enum.KeyCode.E
local COOLDOWN_SECONDS = 1
local character = script.Parent :: Model
local humanoid = character:WaitForChild("Humanoid") :: Humanoid
local animator = humanoid:WaitForChild("Animator") :: Animator
local animation = Instance.new("Animation")
animation.AnimationId = ANIMATION_ID
local track = animator:LoadAnimation(animation)
track.Priority = Enum.AnimationPriority.Action -- above walking and idle, so the legs keep walking but the arms wave
local lastPlayed = -math.huge
UserInputService.InputBegan:Connect(function(input: InputObject, gameProcessed: boolean)
-- gameProcessed is true while the player types in chat or a text box
if gameProcessed or input.KeyCode ~= KEY then
return
end
if humanoid.Health <= 0 or os.clock() - lastPlayed < COOLDOWN_SECONDS then
return
end
lastPlayed = os.clock()
track:Play(0.1) -- fade in over 0.1 seconds
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
StarterPlayer
StarterCharacterScripts
EmoteOnKey LocalScript new
Settings you can change
ANIMATION_IDIn the script- The animation to play. The default is Roblox's own R15 wave. Put the ID of an animation you published from the Animation Editor here. Default:
"rbxassetid://507770239". KEYIn the script- The key that plays it. Default:
Enum.KeyCode.E. COOLDOWN_SECONDSIn the script- The shortest time between two plays, so holding or tapping the key does not restart it over and over. Default:
1.
The default ID is an R15 animation. If your game uses R6 characters (Game Settings > Avatar), use an R6 animation instead: an animation only moves the rig type it was made for.
Type-checked with luau-lsp strict against Roblox's API definitions on 21 October 2026. Not playtested in Studio yet: press Play and watch the Output window before you publish.
Prompt for RoCode
Add an emote to my game: a LocalScript in StarterCharacterScripts that plays an animation on the player's character when they press E, using the Animator in the Humanoid, Action priority and a 1 second cooldown, ignoring key presses while they type in chat.
On this page
How animations play in Roblox
Four objects are involved. You only create one of them by hand: the script makes the rest.
| Object | Where it is | What it does |
|---|---|---|
Animation | Made by your script | Holds the AnimationId, the address of the animation asset. It plays nothing by itself. |
Animator | Inside the character's Humanoid (Roblox adds it) | Loads animations and plays them on the rig's joints. |
AnimationTrack | Returned by Animator:LoadAnimation() | The playable copy: Play(), Stop(), Looped, Priority, Speed and the events. |
| The animation asset | On Roblox, published from the Animation Editor | The keyframes themselves. Its ID is what goes in AnimationId. |
Load each animation once and keep the track. Calling LoadAnimation every time a key is pressed creates a new track each time, and they pile up.
| Priority | Used for |
|---|---|
Core | The lowest. Anything else covers it. |
Idle | Standing still. |
Movement | Walking, running, jumping. |
Action | Emotes, attacks, tools. The usual choice for an animation you play from a script. |
Action2 to Action4 | An action that must cover another action, for example a stun over an attack. |
When two playing tracks move the same joint, the one with the higher priority wins on that joint. An Action wave that only moves the arm lets the legs keep walking underneath it.
Step 1: Get an animation ID you can use
Roblox only plays an animation in your game when the game's owner owns it, or Roblox does. That rules out most IDs copied from the Creator Store or a video: they belong to someone else and fail to load. You have two good sources.
- Make your own. In Studio, open the Avatar tab and choose Animation Editor, select a rig (the Rig Builder on the same tab inserts one), pose it on the timeline, then use the editor's menu to Publish to Roblox. Publish it to the same account or group that owns your game. Copy the ID it gives you.
- Use Roblox's own. The default emotes in Roblox's Animate script, such as the R15 wave (
507770239) and dance (507771019), belong to Roblox, so they play in any game. This guide uses them so you can test straight away.
Write the ID as "rbxassetid://" followed by the number, for example "rbxassetid://507770239".
Step 2: Play an animation when a key is pressed
- In the Explorer, open StarterPlayer, hover over StarterCharacterScripts, click + and insert a LocalScript.
- Rename it
EmoteOnKeyand paste the code below. - Press Play and press E. Your character waves.
-- LocalScript in StarterPlayer > StarterCharacterScripts named EmoteOnKey.
-- Press E to play an animation on your own character. Everyone in the server sees it.
local UserInputService = game:GetService("UserInputService")
local ANIMATION_ID = "rbxassetid://507770239" -- Roblox's own R15 wave; put your animation's ID here
local KEY = Enum.KeyCode.E
local COOLDOWN_SECONDS = 1
local character = script.Parent :: Model
local humanoid = character:WaitForChild("Humanoid") :: Humanoid
local animator = humanoid:WaitForChild("Animator") :: Animator
local animation = Instance.new("Animation")
animation.AnimationId = ANIMATION_ID
local track = animator:LoadAnimation(animation)
track.Priority = Enum.AnimationPriority.Action -- above walking and idle, so the legs keep walking but the arms wave
local lastPlayed = -math.huge
UserInputService.InputBegan:Connect(function(input: InputObject, gameProcessed: boolean)
-- gameProcessed is true while the player types in chat or a text box
if gameProcessed or input.KeyCode ~= KEY then
return
end
if humanoid.Health <= 0 or os.clock() - lastPlayed < COOLDOWN_SECONDS then
return
end
lastPlayed = os.clock()
track:Play(0.1) -- fade in over 0.1 seconds
end)
Scripts in StarterCharacterScripts are copied into the character every time it spawns, so script.Parent is the character and the track is loaded again after each respawn.
gameProcessed is true when the key press was used by Roblox first, for example while the player types in chat. Checking it stops the emote from firing every time someone types the letter E.
Everyone sees the wave, not just you. Roblox replicates animations that a player plays on their own character, as long as the Animator was made by the server, which the one Roblox puts in every character is. That is why this script never needs a RemoteEvent.
Step 3: Turn a looping animation on and off
A dance or a sit should keep going until the player stops it. Set Looped to true and play or stop the same track on each key press. This version also stops the dance when the player starts walking.
-- LocalScript in StarterPlayer > StarterCharacterScripts named DanceToggle.
-- Press G to start a looping dance, press G again (or walk) to stop it.
local UserInputService = game:GetService("UserInputService")
local DANCE_ID = "rbxassetid://507771019" -- Roblox's own R15 dance; put your animation's ID here
local KEY = Enum.KeyCode.G
local character = script.Parent :: Model
local humanoid = character:WaitForChild("Humanoid") :: Humanoid
local animator = humanoid:WaitForChild("Animator") :: Animator
local animation = Instance.new("Animation")
animation.AnimationId = DANCE_ID
local track = animator:LoadAnimation(animation)
track.Priority = Enum.AnimationPriority.Action
track.Looped = true
UserInputService.InputBegan:Connect(function(input: InputObject, gameProcessed: boolean)
if gameProcessed or input.KeyCode ~= KEY then
return
end
if track.IsPlaying then
track:Stop(0.2)
else
track:Play(0.2)
end
end)
-- Stop dancing when the player starts walking
humanoid.Running:Connect(function(speed: number)
if speed > 0.5 and track.IsPlaying then
track:Stop(0.2)
end
end)
The number passed to Play and Stop is a fade time in seconds. A short fade blends the dance in and out instead of snapping between poses.
Step 4: Play an animation on an NPC
An NPC belongs to no player, so its animations are played by the server. Put a Script (not a LocalScript) inside the NPC model. A rig made with the Rig Builder already has a Humanoid, a HumanoidRootPart and an Animator.
-- Script inside the NPC model named NpcAnimation. The NPC needs a Humanoid and a HumanoidRootPart.
-- Plays a looping animation on the NPC from the server, so every player sees it.
local ANIMATION_ID = "rbxassetid://507771019" -- Roblox's own R15 dance; put your animation's ID here
local npc = script.Parent :: Model
local humanoid = npc:WaitForChild("Humanoid") :: Humanoid
-- An NPC made from a rig usually has an Animator already; create one if it does not
local animator = humanoid:FindFirstChildOfClass("Animator")
if not animator then
local newAnimator = Instance.new("Animator")
newAnimator.Parent = humanoid
animator = newAnimator
end
assert(animator, "the NPC has no Animator")
local animation = Instance.new("Animation")
animation.AnimationId = ANIMATION_ID
local track = animator:LoadAnimation(animation)
track.Looped = true
track:Play()
Step 5: Time something to a moment in the animation
A punch should deal damage when the fist lands, not when the key is pressed. In the Animation Editor, right-click the timeline at that moment and add an animation event named Hit, then publish the animation again. In code, track:GetMarkerReachedSignal("Hit") fires at that frame.
-- LocalScript in StarterPlayer > StarterCharacterScripts named PunchOnClick.
-- Plays your punch animation on a click and tells the server the moment the fist lands,
-- using an animation event named "Hit" that you add in the Animation Editor.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local PUNCH_ID = "rbxassetid://0" -- your own punch animation, with a "Hit" event at the moment of impact
local COOLDOWN_SECONDS = 0.6
local punchHit = ReplicatedStorage:WaitForChild("PunchHit") :: RemoteEvent
local character = script.Parent :: Model
local humanoid = character:WaitForChild("Humanoid") :: Humanoid
local animator = humanoid:WaitForChild("Animator") :: Animator
local animation = Instance.new("Animation")
animation.AnimationId = PUNCH_ID
local track = animator:LoadAnimation(animation)
track.Priority = Enum.AnimationPriority.Action
track:GetMarkerReachedSignal("Hit"):Connect(function()
punchHit:FireServer() -- the server checks range and cooldown before it deals damage
end)
local lastPunch = -math.huge
UserInputService.InputBegan:Connect(function(input: InputObject, gameProcessed: boolean)
if gameProcessed or input.UserInputType ~= Enum.UserInputType.MouseButton1 then
return
end
if os.clock() - lastPunch < COOLDOWN_SECONDS then
return
end
lastPunch = os.clock()
track:Play(0.05)
end)
This script needs a RemoteEvent named PunchHit in ReplicatedStorage, and your own punch animation with a Hit event in place of rbxassetid://0.
The client only says "my punch landed". A server Script that listens to PunchHit.OnServerEvent decides whether anyone was in range and how much damage to deal, with its own cooldown, so a player who fires the event by hand cannot hit from across the map. The RemoteEvents guide shows how to check a request like this on the server.
Common errors and fixes
Open the Output window first (Window menu, then Output). Most animation problems print a line there.
Nothing happens and Output says the animation failed to load
The animation is owned by someone other than the game's owner. Publish your own copy from the Animation Editor to the account or group that owns the game, and use that ID.
The animation plays but only the arms move, or it looks half finished
Another track with a higher priority is moving the same joints. Set your track's Priority to Action (or higher), or publish the animation with that priority from the Animation Editor.
Nothing happens and there is no error
Check the rig type: an R15 animation does nothing on an R6 character. Also check the script is a LocalScript in StarterCharacterScripts; a LocalScript in Workspace does not run.
The NPC does not animate
Its parts are anchored, or it has no Animator in its Humanoid. Anchor only the HumanoidRootPart; the Step 4 script creates an Animator if one is missing.
Other players do not see my animation
The animation was played on a character the player does not own, or on an Animator created by a LocalScript. Play your own character's animations from a LocalScript, and everything else (NPCs, other players) from a server Script.
Build it with RoCode
RoCode is an AI agent that works inside Roblox Studio through a plugin. You describe the move, and it writes the scripts in the place you have open.
Add a wave emote on E and a dance I can turn on and off with G, and make the shopkeeper NPC wave at players who walk up to it.
- Writes the LocalScripts and the NPC Script and puts them where they belong
- Can build simple moves such as a wave, a punch or a dance in code, so there is no animation to upload
- Checks the scripts it wrote inside Studio before it finishes
You still press Play and check that the motion looks the way you want. How RoCode connects to Studio.
Questions
Can I use any animation ID I find?
No. An animation only plays in a game whose owner owns it, or when Roblox owns it. Use your own animations, or Roblox's default ones.
Does a LocalScript animation replicate to other players?
Yes, when the player plays it on their own character through the Animator Roblox gives the character. Animations on NPCs should be played from a server Script.
How do I make an animation loop?
Set track.Looped = true before you play it, or tick Loop in the Animation Editor before you publish.
How do I stop an animation?
Call track:Stop() on the track you played. Pass a number, such as track:Stop(0.2), to fade it out over that many seconds.
Why use Animator instead of Humanoid:LoadAnimation?
Humanoid:LoadAnimation is deprecated. Animator:LoadAnimation does the same job and is the one Roblox documents.
Sources and further reading
- Roblox Creator Docs: Use animations. loading and playing animations from scripts, ownership
- Roblox Creator Docs: Animator. LoadAnimation and replication
- Roblox Creator Docs: AnimationTrack. Play, Stop, Looped, Priority, GetMarkerReachedSignal
- Roblox Creator Docs: Animation Editor. making, publishing and animation events
How the code was checked: every script on this page passes Luau's strict type checker against Roblox's API definitions (luau-lsp, 21 October 2026). A type check catches misspelt APIs and wrong types, not game logic, so playtest in Studio before you publish.