How to Animate an NPC in Roblox: Idle, Walk and Custom Animations (2026)

Make a Roblox NPC idle, walk and react with copy-paste scripts: set up the rig, get an animation ID, play it through the Animator, switch idle and walk with pathfinding, and fix the usual reasons it will not play.

Updated By Alex · Founder, NPCMotion13 min read
Example output · R15
Text to motion

“A person sprints forward naturally with fast steps and swinging arms. Loop naturally”

Try this prompt
TL;DR
  • An NPC animates the same way a player does: an Animation with an asset ID, loaded through the Animator inside its Humanoid, then played as a track.
  • Do it from a server Script inside the NPC. A LocalScript in the Workspace never runs, and an NPC’s animations only replicate when the server plays them.
  • Use Animator:LoadAnimation(). The older Humanoid:LoadAnimation() is deprecated.
  • Most “it won’t play” bugs are one of five things: anchored limbs, a missing permission, a low priority, the wrong rig type, or a LocalScript.
  • Need the animations themselves? Generate idles, walks and gestures on R15 from a sentence with NPCMotion, then use the scripts below.

To animate an NPC in Roblox, give it an Animator, load an animation by its asset ID from a server Script, and play the track. That is four lines once the rig is set up. The rest of this guide covers everything around those four lines: where the animation comes from, why it might only play for you, how to switch between idle and walk as the NPC moves, and how to make it react when a player walks up.

How do you set up an NPC rig?

  1. 1Insert a rig. In Studio, open the Character menu on the Avatar or Home tab (the Rig Generator) and choose R15. You get a model with a Humanoid, a HumanoidRootPart and fifteen body parts joined by Motor6Ds.
  2. 2Name it and move it into place. Rename the model to something like “Shopkeeper”. Folders such as Workspace.NPCs keep larger games manageable.
  3. 3Check nothing is anchored except, optionally, the root. Animations move limbs through their Motor6Ds, and an anchored limb cannot move. For an NPC that should never leave its spot, anchoring only the HumanoidRootPart is fine.
  4. 4Make sure it has an Animator. Look inside the Humanoid. If there is no Animator, the scripts below create one on the server, which is where it must be created for animations to replicate.
Your NPC is…UseAnimations from
A person, using the standard avatar shapeHumanoid with an Animator (R15)R15 animations, including generated ones
A person on an old R6 rigHumanoid with an Animator (R6)R6 animations only; R15 clips will not fit
A creature, vehicle or propAnimationController with an AnimatorAnimations keyed on that exact rig

Where do you get the animation?

You need an animation asset ID: the number in rbxassetid://123456789. There are three ways to make the animation behind it.

RouteHowGood for
Animation EditorPose the rig keyframe by keyframe in StudioExact, stylised moves and anything holding props
Animation Capture (beta)Upload a video under 15 seconds of one person, in the Animation EditorQuick body motion you can act out
NPCMotionType the move or upload a phone clip; get an R15 KeyframeSequence with the feet lockedIdles, walks, jobs, gestures and reactions in bulk

Whichever you use, finish in the Animation Editor: open the animation on your NPC, check it, then use Publish to Roblox and copy the ID it gives you. Publish it under the same creator as the game, which for a group game means choosing the group.

Why does my NPC animation only play for me?

Usually permissions. Since July 2025, an experience needs permission to load an animation asset. Your own animations are always available in your own games, and playtesting in Studio grants permission automatically. Problems start when the animation and the game have different owners.

  • Group game, personal animation. Publish the animation under the group instead, or grant the experience access from the animation’s Permissions page on the Creator Hub.
  • Someone else’s animation. Animations cannot be set to open use. Ask the owner to grant your experience access, or make your own.
  • It worked yesterday. Check the Output window for a permission or asset failure on the ID. A failed load is reported there even when nothing else looks wrong.

How do you play an animation on an NPC?

Put a Script (not a LocalScript) inside the NPC model and replace the ID with yours. This loops an idle forever, which is all a shopkeeper or a guard needs:

Script inside the NPC model
-- Script (not LocalScript) inside the NPC model
local npc = script.Parent
local humanoid = npc:WaitForChild("Humanoid")

-- Use the NPC's Animator, creating one on the server if it has none
local animator = humanoid:FindFirstChildOfClass("Animator")
if not animator then
	animator = Instance.new("Animator")
	animator.Parent = humanoid
end

local idleAnimation = Instance.new("Animation")
idleAnimation.AnimationId = "rbxassetid://YOUR_IDLE_ID"

local idleTrack = animator:LoadAnimation(idleAnimation)
idleTrack.Priority = Enum.AnimationPriority.Idle
idleTrack.Looped = true
idleTrack:Play()

Two details matter. Load each animation once and keep the track: every LoadAnimation call makes a new track, and loading in a loop leaks them. And set Looped in the script rather than trusting the setting the animation was published with, so the behaviour is visible in the code.

How do you make an NPC walk with a walk animation?

Players get walking for free from the Animate script in their character. That script is a LocalScript built for players, so an NPC you move with Humanoid:MoveTo() slides along in its idle pose. The fix is a small controller that listens to the Humanoid’s Running event and switches tracks, plus pathfinding so it walks around walls.

Script inside the NPC model (put a part named Target in the Workspace)
-- Script inside the NPC model. Walks to a part named Target,
-- playing walk while moving and idle while standing.
local PathfindingService = game:GetService("PathfindingService")

local npc = script.Parent
local humanoid = npc:WaitForChild("Humanoid")
local root = npc:WaitForChild("HumanoidRootPart")
root:SetNetworkOwner(nil) -- the server simulates the NPC: no hand-off stutter

local animator = humanoid:FindFirstChildOfClass("Animator")
if not animator then
	animator = Instance.new("Animator")
	animator.Parent = humanoid
end

local function load(id, priority)
	local animation = Instance.new("Animation")
	animation.AnimationId = id
	local track = animator:LoadAnimation(animation)
	track.Priority = priority
	track.Looped = true
	return track
end

-- Your own clips, or Roblox's default R15 idle and walk
local idle = load("rbxassetid://507766666", Enum.AnimationPriority.Idle)
local walk = load("rbxassetid://507777826", Enum.AnimationPriority.Movement)
idle:Play()

-- Running fires with the NPC's speed, and with 0 when it stops
humanoid.Running:Connect(function(speed)
	if speed > 0.5 then
		if not walk.IsPlaying then
			walk:Play(0.2)
			idle:Stop(0.2)
		end
		walk:AdjustSpeed(speed / 16) -- keep feet in step at other WalkSpeeds
	elseif walk.IsPlaying then
		walk:Stop(0.2)
		idle:Play(0.2)
	end
end)

local function walkTo(goal)
	local path = PathfindingService:CreatePath({ AgentRadius = 2, AgentHeight = 5, AgentCanJump = true })
	local ok = pcall(function()
		path:ComputeAsync(root.Position, goal)
	end)
	if not ok or path.Status ~= Enum.PathStatus.Success then
		return false
	end
	for _, waypoint in path:GetWaypoints() do
		if waypoint.Action == Enum.PathWaypointAction.Jump then
			humanoid.Jump = true
		end
		humanoid:MoveTo(waypoint.Position)
		-- MoveTo gives up after 8 seconds; false means it never arrived
		if not humanoid.MoveToFinished:Wait() then
			return false
		end
	end
	return true
end

walkTo(workspace:WaitForChild("Target").Position)
  • SetNetworkOwner(nil) keeps the NPC simulated on the server. Without it, physics ownership can pass to a nearby player and the walk stutters as it changes hands.
  • AdjustSpeed(speed / 16) speeds the walk cycle up or down with the NPC’s actual speed, so feet do not slide when you change WalkSpeed.
  • MoveTo times out after eight seconds. Walking waypoint by waypoint, as above, keeps each call short. For one long MoveTo, call it again before the eight seconds are up.
  • The two IDs are Roblox’s default R15 idle and walk. To double-check them, play solo, open your character’s Animate script and look at its idle and walk values.
Example output · R15
Text to motion

“A person limps forward favoring the injured right leg with slow uneven steps”

A walk that tells a story: swap this in for the default walk ID and the same controller plays it, with no change to the script.

Try this prompt

Default animations make every NPC move like every player. A tired farmer, a nervous thief and a proud knight should not share a walk. Swap the two IDs for your own and the same script gives each character its own personality.

How do you play an animation when a player walks up?

A ProximityPrompt is the simplest trigger: it shows a key prompt when a player is close and fires Triggered when they press it. Insert one into the NPC’s HumanoidRootPart, then add this Script to the NPC:

Script inside the NPC model
-- Script inside the NPC model. Add a ProximityPrompt to the
-- HumanoidRootPart, and put your wave animation's ID below.
local npc = script.Parent
local humanoid = npc:WaitForChild("Humanoid")
local root = npc:WaitForChild("HumanoidRootPart")
local prompt = root:WaitForChild("ProximityPrompt")

local animator = humanoid:FindFirstChildOfClass("Animator")
if not animator then
	animator = Instance.new("Animator")
	animator.Parent = humanoid
end

local waveAnimation = Instance.new("Animation")
waveAnimation.AnimationId = "rbxassetid://YOUR_WAVE_ID"
local wave = animator:LoadAnimation(waveAnimation) -- load once, play many times
wave.Priority = Enum.AnimationPriority.Action
wave.Looped = false

prompt.ActionText = "Talk"
prompt.Triggered:Connect(function(player)
	local character = player.Character
	local playerRoot = character and character:FindFirstChild("HumanoidRootPart")
	if playerRoot then
		-- Turn to face the player, staying upright
		local target = Vector3.new(playerRoot.Position.X, root.Position.Y, playerRoot.Position.Z)
		root.CFrame = CFrame.lookAt(root.Position, target)
	end
	if not wave.IsPlaying then
		wave:Play()
	end
end)

The wave plays at Action priority, so it layers over the idle without stopping it, and when the wave ends the idle is still underneath. The same pattern drives a shopkeeper who nods when you buy something or a guard who raises a hand to stop you: one track per reaction, loaded once.

Example output · R15
Text to motion

“Look scared then turn and run away with big dramatic strides”

A bigger reaction is still just another track: a villager who startles and runs from the player is loaded and played exactly like the wave.

Try this prompt

What do animation priorities do?

When two tracks move the same joint, the higher priority wins. Getting this right is what lets an NPC wave while it idles, or swing a sword while it walks.

PriorityUse it forNotes
CoreNothing of yoursThe lowest. Roblox’s default animations play here, so anything you set higher overrides them
IdleStanding, breathing, looking aroundLoops underneath everything else
MovementWalks, runs, climbsOverrides idle while the NPC moves
ActionWaves, attacks, emotes, reactionsPlays over idle and movement
Action2 to Action4Something that must win over another actionAction4 is the highest priority there is

Should NPC animations run on the server or the client?

On the server, for any NPC every player should see. Roblox only replicates animations on non-player rigs when the server loads and plays them, and the Animator itself must be created on the server.

Client-side playback is a deliberate optimisation for crowds that only need to look alive to one player, such as background pedestrians in a city. Each player then animates their own copy, and nobody sees anyone else’s.

Why won’t my NPC animation play?

What you seeLikely causeFix
Nothing happens, no errorsThe code is in a LocalScript in the Workspace, which never runsUse a Script inside the NPC
Plays in Studio, not for othersThe experience has no permission for the animationPublish under the game’s owner, or grant access on the Creator Hub
Some limbs move, others are frozenThose parts are anchoredUnanchor everything except, optionally, the HumanoidRootPart
The NPC snaps into a strange poseAn R6 animation on an R15 rig, or the reverseUse an animation made for that rig type
It plays once and stopsLooped is falseSet track.Looped = true before playing
The wave never showsAnother track on the same joints has a higher priorityRaise the wave to Action, or lower the other track
It walks in its idle poseNPCs have no Animate scriptUse the walk controller above
The walk stutters near playersPhysics ownership passing between server and playersroot:SetNetworkOwner(nil)
A deprecation warningHumanoid:LoadAnimation()Load through the Animator instead

How do you make lots of NPC animations quickly?

A living town needs dozens of clips: idles for every job, walks with personality, reactions to the player. Hand-keying each one is where most solo projects stall. Generating them takes that column off your plate.

  1. 1List every clip your NPCs need before you make any. The table in our 50 Roblox game ideas shows what a typical game needs.
  2. 2Generate them in NPCMotion by typing each move, or record yourself on a phone for anything with tricky timing. Foot contacts are locked and limbs are re-solved for R15.
  3. 3Bring them into Studio as a .rbxm or a pasted script, check each on its NPC in the Animation Editor, and publish.
  4. 4Paste the IDs into the scripts above.

Sources and further reading (checked Oct 6, 2026)

Frequently asked questions

Written by Alex, Founder, NPCMotion. Builds NPCMotion, the text and video to R15 animation tool for Roblox creators, and writes up the parts of the pipeline that are hardest to get right: retargeting, foot contacts and GPU inference.

Published

Keep reading

All guides