Another Open Sourced Ragdoll System

Current Version

Version 1.6.9

Newest change:

· performance improvements // 21.02.2026 (Version 1.6.9)
-> increased performance by not always creating new instances at ragdoll (currently just for 'Stable')
-> changed some code styling

INTRODUCTION

Hey Devforum, so I just made another Ragdoll System, I know there are like quadrillion other ones to use out there, but I still decided to make my own. I am being honest, I can’t really say why you should use this over other ones but

  1. I am using this for myself, so it will receive continued support
  2. It is easy to use
  3. It has some easy to configure settings

DISCLAIMER

Keep in mind, I am definitely not the best scripter, so some code may not be very good.

SCRIPTS (Version 1.6.8)

R6RagdollServer
--[[ System By @Liam 
-> Version 1.6.8
 ♥ Thanks for using this!! ♥ 
--]] 



--||Services||--
local Players = game:GetService("Players")
local Debris = game:GetService("Debris")

--||Ragdoll CFrames (can be changed)||--
local attachmentCFrames = {
	--HEAD
	["Neck"] = {CFrame.new(0, 1, 0, 0, -1, 0, 1, 0, -0, 0, 0, 1), CFrame.new(0, -0.5, 0, 0, -1, 0, 1, 0, -0, 0, 0, 1)},
	--ARMS	
	["Left Shoulder"] = {CFrame.new(-1.3, 0.75, 0, -1, 0, 0, 0, -1, 0, 0, 0, 1), CFrame.new(0.2, 0.75, 0, -1, 0, 0, 0, -1, 0, 0, 0, 1)},
	["Right Shoulder"] = {CFrame.new(1.3, 0.75, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1), CFrame.new(-0.2, 0.75, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1)},
	--LEGS	
	["Left Hip"] = {CFrame.new(-0.5, -1, 0, 0, 1, -0, -1, 0, 0, 0, 0, 1), CFrame.new(0, 1, 0, 0, 1, -0, -1, 0, 0, 0, 0, 1)},
	["Right Hip"] = {CFrame.new(0.5, -1, 0, 0, 1, -0, -1, 0, 0, 0, 0, 1), CFrame.new(0, 1, 0, 0, 1, -0, -1, 0, 0, 0, 0, 1)},
}

--||Don't change this||--
local ragdollInstanceNames = {
	RagdollAttachment = true,
	RagdollConstraint = true,
	ColliderPart = true,
}

--||Settings||--
local ragdoll = {
	ragdollSounds = true, --if you wanna have ragdoll sounds when the body hits something set this to true
	ragdollOnDeath = true, --if you want to automatically enable ragdoll on death
	ragdollOnGameStart = true, --if you want every character that can be found in workspace at the start of the game to use this ragdoll system
}

local constrainsAndWelds = {
	LimitBSC = true, --if you want to have a rotation limit for the BallSocketConstraint
}

local fixes = {
	VoidBug = true, --fixes the bug that the player doesnt respawn when they fall into the void 
}

local npcSettings = {
	fixNpcFling = true, --fixes the npc flinging away when they get up (this sets the enemies hrp networkownership to nil)	
	npcFolders = {}, --if you have specific npc folders add them in the table, example: npcFolders = {workspace.Enemies, workspace.Enemies2}
} 

------------------------------------------------------------------------------------------------------------------





--//
local function createImpactSound(parent, soundScript)
	local sound = soundScript:Clone()
	sound.Parent = parent
	sound.Disabled = false
end

--//playing a sound when those body parts hit something
local function PlayRagdollSounds(char) 
	createImpactSound(char.Head, script.ImpactSound)
	createImpactSound(char["Left Arm"], script.ImpactSound)
	createImpactSound(char["Right Arm"], script.ImpactSound)	
end

--//checking if a model is a valid Character
local function isCharacterModel(model)
	return model:IsA("Model") and model:FindFirstChildWhichIsA("Humanoid")
end

--//creating the parts that are gonna be colliding with the world
local function createColliderPart(part) 
	if not part then return end
	local rp = Instance.new("Part")
	rp.Name = "ColliderPart"
	rp.Size = part.Size / 1.7
	rp.Massless = true
	rp.CFrame = part.CFrame
	rp.Transparency = 1

	local wc = Instance.new("WeldConstraint")
	wc.Part0 = rp
	wc.Part1 = part
	wc.Parent = rp
	rp.Parent = part
end

--//replacing the joints and other stuff
local function replaceJoints(char, hum) 
	local hrp = char:FindFirstChild("HumanoidRootPart")

	hum.PlatformStand = true
	hum.AutoRotate = false
	if not hrp then return end
	hrp.Massless = true
	hrp.CanCollide = false

	for _, motor in char:GetDescendants() do
		if motor:IsA("Motor6D") and attachmentCFrames[motor.Name] then
			motor.Enabled = false
			
			local a0, a1 = Instance.new("Attachment"), Instance.new("Attachment")
			a0.CFrame = attachmentCFrames[motor.Name][1]
			a1.CFrame = attachmentCFrames[motor.Name][2]

			a0.Name = "RagdollAttachment"
			a1.Name = "RagdollAttachment"

			createColliderPart(motor.Part1)

			local b = Instance.new("BallSocketConstraint")
			b.Attachment0 = a0
			b.Attachment1 = a1
			b.Name = "RagdollConstraint"

			b.Radius = 0.15
			b.LimitsEnabled = constrainsAndWelds.LimitBSC
			b.TwistLimitsEnabled = motor.Name == "Neck"
			b.MaxFrictionTorque = 0
			b.Restitution = 0
			b.UpperAngle = motor.Name == "Neck" and 45 or 90
			b.TwistLowerAngle = motor.Name == "Neck" and -70 or -45
			b.TwistUpperAngle = motor.Name == "Neck" and 70 or 45

			a0.Parent = motor.Part0
			a1.Parent = motor.Part1
			b.Parent = motor.Parent
		end
	end
	
	if ragdoll.ragdollSounds then PlayRagdollSounds(char) end
end

--//resetting the joints and all the other properties
local function resetJoints(hum) 
	local char = hum.Parent
	local hrp = char.HumanoidRootPart

	char:SetAttribute("IsRagdoll", false)
	hum.PlatformStand = false
	hum.AutoRotate = true
	hrp.Massless = false
	hrp.CanCollide = true 
	
	if ragdoll.ragdollSounds then
		for _, Script in char:GetDescendants() do
			if Script:IsA("Script") and Script.Name == "ImpactSound" then
				Script:Destroy()
			end
		end
	end	

	if hum.Health <= 0 then return end --we don't want the player to stand up again if they are dead	
	
	for _, part in char:GetDescendants() do
		if part:IsA("BasePart") then
			part.AssemblyLinearVelocity = Vector3.zero
			part.AssemblyAngularVelocity = Vector3.zero
		end
	end
	
	for _, instance in char:GetDescendants() do
		if ragdollInstanceNames[instance.Name] then
			instance:Destroy()
		end
		if instance:IsA("Motor6D") then
			instance.Enabled = true
		end
	end

	hum:SetStateEnabled(Enum.HumanoidStateType.Ragdoll, false)
	hum:SetStateEnabled(Enum.HumanoidStateType.FallingDown, false)
	hum:SetStateEnabled(Enum.HumanoidStateType.GettingUp, true)

	local isPlayer = Players:GetPlayerFromCharacter(hum.Parent)
	if not isPlayer and npcSettings["fixNpcFling"] == true then 
		hrp:SetNetworkOwner(nil)
		local float = Instance.new("BodyPosition") 
		float.Position = hrp.Position + vector.create(0, 2, 0)
		float.MaxForce = Vector3.new(1e5, 1e5, 1e5)
		float.P = 1e4
		float.Parent = hrp
		Debris:AddItem(float, 0.25)

		local stabilizer = Instance.new("BodyGyro") 
		stabilizer.P = 1e6
		stabilizer.D = 500
		stabilizer.MaxTorque = vector.create(1e4, 0, 1e4)
		stabilizer.Parent = hrp
		Debris:AddItem(stabilizer, .25)
	end

end


--//applying the ragdoll
local function applyRagdollToCharacter(char) --
	local hum = char:FindFirstChild("Humanoid")
	local torso = char:FindFirstChild("Torso")
	if hum and torso then
		hum.BreakJointsOnDeath = not ragdoll.ragdollOnDeath
		hum.RequiresNeck = not ragdoll.ragdollOnDeath

		char:SetAttribute("IsRagdoll", false) --setting ragdoll to false from the beginning on

		char:GetAttributeChangedSignal("IsRagdoll"):Connect(function()
			if char:GetAttribute("IsRagdoll") then
				replaceJoints(char, hum)
			else
				resetJoints(hum)
			end
		end)

		hum.Died:Once(function() --on death
			if not ragdoll.ragdollOnDeath then return end
			char:SetAttribute("IsRagdoll", true)
			torso:ApplyImpulse(torso.CFrame.LookVector * 100)
		end)
	end
end


--//
local function applyRagdollToAllCharacters()
	for _, char in workspace:GetDescendants() do
		if isCharacterModel(char) then
			applyRagdollToCharacter(char)
		end	
	end
end

if ragdoll.ragdollOnGameStart then applyRagdollToAllCharacters() end


--//applying the ragdoll to the specific npc folders
for _, folder in npcSettings.npcFolders do
	for _, char in folder:GetChildren() do
		applyRagdollToCharacter(char)
	end
	
	folder.ChildAdded:Connect(function(child)
		if isCharacterModel(child) then
			applyRagdollToCharacter(child)
		end
	end)
end



--//
Players.PlayerAdded:Connect(function(plr)
	plr.CharacterAdded:Connect(function(char)
		applyRagdollToCharacter(char)
		
		if fixes.VoidBug then
			char.ChildRemoved:Connect(function() --this fixes the void bug
				if char.WorldPivot.Position.Y <= workspace.FallenPartsDestroyHeight then	
					char.Humanoid.Health = 0 --so that the player just dies normaly when they fall into the void
				end
			end)
		end
		
	end)
end)
R15RagdollServer
--[[ System By @Liam 
-> Version 1.6.8
 ♥ Thanks for using this!! ♥ 
--]] 



--||Services||--
local Players = game:GetService("Players")
local Debris = game:GetService("Debris")

--||Ragdoll CFrames (can be changed)||-- 
local attachmentCFrames = {
	--HEAD
	["Neck"] = {CFrame.new(0, .75, 0, 0, -1, 0, 1, 0, -0, 0, 0, 1), CFrame.new(0, -0.5, 0, 0, -1, 0, 1, 0, -0, 0, 0, 1)},	
	--ARMS	
	["LeftShoulder"] = {CFrame.new(-.75, 0.75, 0), CFrame.new(0, 0.75, 0)},
	["RightShoulder"] = {CFrame.new(.75, 0.75, 0), CFrame.new(-0, 0.75, 0)},
	--
	["LeftElbow"] = {CFrame.new(-0, 0, 0), CFrame.new(0, 0.6, 0)},
	["RightElbow"] = {CFrame.new(0, 0, 0), CFrame.new(-0, 0.6, 0)},
	--
	["LeftWrist"] = {CFrame.new(-0, 0, 0), CFrame.new(0, .6, 0)},
	["RightWrist"] = {CFrame.new(0, 0, 0), CFrame.new(-0, .6, 0)},	
	--LEGS	
	["LeftHip"] = {CFrame.new(-0.5, -0, 0), CFrame.new(0, .75, 0)},
	["RightHip"] = {CFrame.new(0.5, -0, 0), CFrame.new(0, .75, 0)},	
	--
	["LeftKnee"] = {CFrame.new(-0, -0, 0), CFrame.new(0, .6, 0)},
	["RightKnee"] = {CFrame.new(0, -0, 0), CFrame.new(0, .6, 0)},
	--
	["LeftAnkle"] = {CFrame.new(-0, -0, 0), CFrame.new(0, .6, 0)},
	["RightAnkle"] = {CFrame.new(0, -0, 0), CFrame.new(0, .6, 0)},	
	--TORSO	
	["Root"] = {CFrame.new(0, 0, 0), CFrame.new(0, 0, 0)},
	--["Waist"] = {CFrame.new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), CFrame.new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)},
}

--||Don't change this||--
local ragdollInstanceNames = {
	RagdollAttachment = true,
	RagdollConstraint = true,
	ColliderPart = true,
}

--||Settings||--
local ragdoll = {
	ragdollSounds = true, --if you wanna have ragdoll sounds when the body hits something set this to true
	ragdollOnDeath = true, --if you want to automatically enable ragdoll on death
	ragdollOnGameStart = true, --if you want every character that can be found in workspace at the start of the game to use this ragdoll system
}

local constrainsAndWelds = {
	LimitBSC = true, --if you want to have a rotation limit for the BallSocketConstraint
}

local fixes = {
	VoidBug = true, --fixes the bug that the player doesnt respawn when they fall into the void 
}

local npcSettings = {
	fixNpcFling = true, --fixes the npc flinging away when they get up (this sets the enemies hrp networkownership to nil)	
	npcFolders = {}, --if you have specific npc folders add them in the table, example: npcFolders = {workspace.Enemies, workspace.Enemies2}
} 

------------------------------------------------------------------------------------------------------------------





--//
local function createImpactSound(parent, soundScript)
	local sound = soundScript:Clone()
	sound.Parent = parent
	sound.Disabled = false
end

--//playing a sound when those body parts hit something
local function PlayRagdollSounds(char) 
	createImpactSound(char.Head, script.ImpactSound)
	createImpactSound(char.LeftLowerArm, script.ImpactSound)
	createImpactSound(char.RightLowerArm, script.ImpactSound)
end

--//checking if a model is a valid Character
local function isCharacterModel(model)
	return model:IsA("Model") and model:FindFirstChildWhichIsA("Humanoid")
end

--//creating the parts that are gonna be colliding with the world
local function createColliderPart(part) 
	if not part then return end
	local rp = Instance.new("Part")
	rp.Name = "ColliderPart"
	rp.Size = part.Size / 1.7
	rp.Massless = true
	rp.CFrame = part.CFrame
	rp.Transparency = 1

	local wc = Instance.new("WeldConstraint")
	wc.Part0 = rp
	wc.Part1 = part
	wc.Parent = rp
	rp.Parent = part
end

--//replacing the joints and other stuff
local function replaceJoints(char, hum) 
	local hrp = char:FindFirstChild("HumanoidRootPart")

	hum.PlatformStand = true
	hum.AutoRotate = false
	if not hrp then return end
	hrp.Massless = true
	hrp.CanCollide = false

	for _, motor in char:GetDescendants() do
		if motor:IsA("Motor6D") and attachmentCFrames[motor.Name] then
			motor.Enabled = false

			local a0, a1 = Instance.new("Attachment"), Instance.new("Attachment")
			a0.CFrame = attachmentCFrames[motor.Name][1]
			a1.CFrame = attachmentCFrames[motor.Name][2]

			a0.Name = "RagdollAttachment"
			a1.Name = "RagdollAttachment"

			createColliderPart(motor.Part1)

			local b = Instance.new("BallSocketConstraint")
			b.Attachment0 = a0
			b.Attachment1 = a1
			b.Name = "RagdollConstraint"

			b.Radius = 0.15
			b.LimitsEnabled = constrainsAndWelds.LimitBSC
			b.TwistLimitsEnabled = motor.Name == "Neck"
			b.MaxFrictionTorque = 0
			b.Restitution = 0
			b.UpperAngle = motor.Name == "Neck" and 45 or 90
			b.TwistLowerAngle = motor.Name == "Neck" and -70 or -45
			b.TwistUpperAngle = motor.Name == "Neck" and 70 or 45

			a0.Parent = motor.Part0
			a1.Parent = motor.Part1
			b.Parent = motor.Parent
		end
	end
	
	if ragdoll.ragdollSounds then PlayRagdollSounds(char) end
end

--//resetting the joints and all the other properties
local function resetJoints(hum) 
	local char = hum.Parent
	local hrp = char.HumanoidRootPart

	char:SetAttribute("IsRagdoll", false)
	hum.PlatformStand = false
	hum.AutoRotate = true
	hrp.Massless = false
	hrp.CanCollide = true    

	if ragdoll.ragdollSounds then
		for _, Script in char:GetDescendants() do
			if Script:IsA("Script") and Script.Name == "ImpactSound" then
				Script:Destroy()
			end
		end
	end	

	if hum.Health <= 0 then return end --we don't want the player to stand up again if they are dead	
	
	for _, part in char:GetDescendants() do
		if part:IsA("BasePart") then
			part.AssemblyLinearVelocity = Vector3.zero
			part.AssemblyAngularVelocity = Vector3.zero
		end
	end
	
	for _, instance in char:GetDescendants() do
		if ragdollInstanceNames[instance.Name] then
			instance:Destroy()
		end
		if instance:IsA("Motor6D") then
			instance.Enabled = true
		end
	end

	hum:SetStateEnabled(Enum.HumanoidStateType.Ragdoll, false)
	hum:SetStateEnabled(Enum.HumanoidStateType.FallingDown, false)
	hum:SetStateEnabled(Enum.HumanoidStateType.GettingUp, true)

	local isPlayer = Players:GetPlayerFromCharacter(hum.Parent)
	if not isPlayer and npcSettings["fixNpcFling"] == true then 
		hrp:SetNetworkOwner(nil)
		local float = Instance.new("BodyPosition") 
		float.Position = hrp.Position + vector.create(0, 2, 0)
		float.MaxForce = Vector3.new(1e5, 1e5, 1e5)
		float.P = 1e4
		float.Parent = hrp
		Debris:AddItem(float, 0.25)

		local stabilizer = Instance.new("BodyGyro") 
		stabilizer.P = 1e6
		stabilizer.D = 500
		stabilizer.MaxTorque = vector.create(1e4, 0, 1e4)
		stabilizer.Parent = hrp
		Debris:AddItem(stabilizer, .25)
	end

end


--//applying the ragdoll
local function applyRagdollToCharacter(char) 
	local hum = char:FindFirstChild("Humanoid")
	local upperTorso = char:FindFirstChild("UpperTorso")
	if hum and upperTorso then
		hum.BreakJointsOnDeath = not ragdoll.ragdollOnDeath
		hum.RequiresNeck = not ragdoll.ragdollOnDeath

		char:SetAttribute("IsRagdoll", false) --setting ragdoll to false from the beginning on

		char:GetAttributeChangedSignal("IsRagdoll"):Connect(function()
			if char:GetAttribute("IsRagdoll") then
				replaceJoints(char, hum)
			else
				resetJoints(hum)
			end
		end)

		hum.Died:Once(function() --on death
			if not ragdoll.ragdollOnDeath then return end
			char:SetAttribute("IsRagdoll", true)
			upperTorso:ApplyImpulse(upperTorso.CFrame.LookVector * 100)
		end)	
	end
end


--//
local function applyRagdollToAllCharacters()
	for _, char in workspace:GetDescendants() do
		if isCharacterModel(char) then
			applyRagdollToCharacter(char)
		end	
	end
end

if ragdoll.ragdollOnGameStart then applyRagdollToAllCharacters() end


--//applying the ragdoll to the specific npc folders
for _, folder in npcSettings.npcFolders do
	for _, char in folder:GetChildren() do
		applyRagdollToCharacter(char)
	end
	
	folder.ChildAdded:Connect(function(child)
		if isCharacterModel(child) then
			applyRagdollToCharacter(child)
		end
	end)
end



--//
Players.PlayerAdded:Connect(function(plr)
	plr.CharacterAdded:Connect(function(char)
		applyRagdollToCharacter(char)

		if fixes.VoidBug then
			char.ChildRemoved:Connect(function() --this fixes the void bug
				if char.WorldPivot.Position.Y <= workspace.FallenPartsDestroyHeight then	
					char.Humanoid.Health = 0 --so that the player just dies normaly when they fall into the void
				end
			end)
		end

	end)
end)
RagdollClient
--[[ System By @Liam 
-> Version 1.6.8
 ♥ Thanks for using this!! ♥ 
--]] 



--||Services||--
local Players = game:GetService("Players")

--||Character||--
local plr = Players.LocalPlayer
local char = plr.Character 
local hum = char:WaitForChild("Humanoid")
local torso = char:FindFirstChild("Torso") or char:FindFirstChild("UpperTorso")

print("This game uses 'Liam's Ragdoll System' V 1.6.8!") -- please leave this in as a support!

------------------------------------------------------------------------------------------------------------------





--//when the player gets ragdolled / unRagdolled
char:GetAttributeChangedSignal("IsRagdoll"):Connect(function()
	local isRagdoll = char:GetAttribute("IsRagdoll")
	if isRagdoll and torso then
		hum:ChangeState(Enum.HumanoidStateType.Ragdoll)
		hum:SetStateEnabled(Enum.HumanoidStateType.GettingUp, false)
		torso:ApplyImpulse(torso.CFrame.LookVector * 75) --push!
	else
		hum:ChangeState(Enum.HumanoidStateType.GettingUp)
	end
end)

--//this happens when the player dies
hum.Died:Connect(function()
	hum.Health = 0
	if not torso then return end
	torso:ApplyImpulse(torso.CFrame.LookVector * 100)
end)

THE MODEL

https://create.roblox.com/store/asset/140489636987227/Liams-Ragdoll-System

HOW TO USE (KINDA NOT SO UP TO DATE)

END

Not much more to say, have a nice day. If you find bugs, tell me and I will try to fix them!


Random Pic Here

Cute cat here:
[spoiler]
CAT
[/spoiler]


Credits

Credits for the Original Script made by @CompletedLoop

[DEPRECATED] Perfect R6 Ragdoll - Easiest Ragdoll System for R6 Avatars

28 Likes

Update Version 1.4.3:

· If "ragdollOnDeath" is false it just breaks your character like normal // 01.11.2025
· Fixed not being able to reset when ragdolled // 01.11.2025
-> It was kinda fixed by just setting the humanoid health to 0 on the client, however I am still searching for a better method cuz it rn looks kinda weird, but it kinda works!
-> the only problem is that it not works when the character never again gets up, so when you just set IsRagdoll to true and then reset but never set it to false again before then you will not respawn.

example: 

char:SetAttribute("IsRagdoll", true)
--reset here and you will respawn
task.wait(2)
char:SetAttribute("IsRagdoll", false)

------------------------------------------------------------

char:SetAttribute("IsRagdoll", true)
--reset here and you will not respawn

Btw if anyone knows how to fix it good, it would be appreciated.
The problem is that the humanoid died not fires when they reset while being ragdolled

If the problem isn’t present for other forms of dying, you can just do :SetCore(“ResetButtonCallback”, someBindableEvent) to detect when the player wants to reset.

Also, side note, what in the heavens is that font you’re using for script editor :sob:

1 Like

I know about that, but this can only be done on the client, and I would rather have a direct fix on the Server. I mean, if I would do it on the client I could also just use remote events you know- but I am searching for a method to avoid that, that’s my point.

Is there something wrong with my font, lol? I really like it and think it’s nice to read.

Ok so I think I found out why, you’re using the Ragdoll humanoid state which can’t actually be automatically changed by the Humanoid and has to be changed manually with :ChangeState() (it says so in the docstring!), and since Dead is a humanoid state it kinda breaks that.

The solution, I think, would be to just find an alternative to the Ragdoll humanoid state. It is deprecated, afterall.

Also, it’s not that there necessarily anything wrong about your font, it’s just that it looks too contrasted and bold for me, my bad if it’s offensive though xd

Alright, thanks for the Info. I’ll look into it when I have time.

And nah everythings fine, no worries xD

Hi, Is this still in development/is this still functional and optimized?

@Cedestius

Yep it’s still being updated and actively worked on,

I’ve actually just published an update like 10 minutes ago!

Updates, version 1.6.0

· experimental new death ragdoll, // 04.22.2025 (Version 1.6.0)
-> added a new experimental version for the R15 & R6 ragdoll scripts
-> the difference, it creates a fake character when they die! (why? why not! Fake characters are in my opinion cooler because you can just actually do more with them)
-> there is also a 'stayTime' setting for the fake character
-> what else is new in the 'Experimental' version? A bug got fixed that the 'Stable' version had where when you die-
-> locally while being ragdolled (like through resetting) your character never actually dies!
-> how was it fixed?
-> we simply send a remote event call to the server when we die on the client to trigger the death on the server for scenarieos like these
-> MORE SETTINGS IN THE NEAR FUTURE!!!!

I also added a FAQ tab in the INFORMATIONS script, (in general, just read the INFORMATIONS script for more detailed infos!)

1 Like

I did credit you inside the model.

Hello, I just want to report a small bug I had while using this ragdoll system. I had this issue that when a player dies the death sound plays twice, instead of once. I believe the reason for this is that because ragdoll on death was on, the client ragdoll sets the state of the humanoid from dead to ragdoll, and the humanoid immediately sets it back to dead, which fires the dead event twice, making the oof sound play twice. To fix this I added a check in the client ragdoll on line 29

if hum.Health ~= 0 then
	hum:ChangeState(Enum.HumanoidStateType.Ragdoll)
end

very cool and intuitive Good job!


I got this error when someone died, and they couldn’t respawn