GripModule is a module that replaces Roblox’s default RightGrip weld with a custom Motor6D joint.
It gives you full control over how and where a tool is held in the player’s hand, including precise position and rotation, which is especially useful for custom animations such as:
Realistic weapon stances
Reloading or aiming animations
Interaction systems where RightGrip limits animation freedom.
Features:
Automatically replaces RightGrip with a Motor6D joint
Allows precise control of grip offset and orientation
Cleans up automatically when the tool is unequipped
Safe to reinitialize (no duplicates)
Works on both Client and Server (recommended to use on both).
You don’t need to credit me for using this module, but if you do, I really appreciate it!
Module
-- // Author: LwgoVlr
local RunService = game:GetService("RunService")
return function(Tool, Part0Name, Part1, Offset)
local OffsetCFrame = Offset or CFrame.new()
local Motor6D = nil
local Equipped = false
local Connections = {}
local function FindRightArm(Char)
if Part0Name then
return Char:FindFirstChild(Part0Name)
end
return Char:FindFirstChild("RightHand") or Char:FindFirstChild("Right Arm")
end
local function CleanupMotor()
if Motor6D then
Motor6D:Destroy()
Motor6D = nil
end
end
table.insert(Connections, Tool.Equipped:Connect(function()
Equipped = true
local Char = Tool.Parent
if not Char or not Char:IsA("Model") then
warn("[GripModule]: Tool parent is not a character.")
return
end
local Part0 = FindRightArm(Char)
if not Part0 then
warn("[GripModule]: Failed to locate Right Arm/Hand in character.")
return
end
local Handle = Part1 or Tool:FindFirstChild("Handle")
if not Handle or not Handle:IsA("BasePart") then
warn("[GripModule]: Missing valid handle part.")
return
end
local RA = Char:FindFirstChild("Right Arm")
if RA then
task.spawn(function()
local Grip = Part0:WaitForChild("RightGrip")
if Grip then Grip:Destroy() end
end)
else
RA = Char:FindFirstChild("RightHand")
if RA then
local Grip = Part0:WaitForChild("RightGrip")
if Grip then Grip:Destroy() end
else
warn("[GripModule]: Right Arm not found in character.")
end
end
CleanupMotor()
local IsServer = RunService:IsServer()
Motor6D = Instance.new("Motor6D")
Motor6D.Name = "Tool6D_"..(if IsServer then "Server" else "Client")
Motor6D.Part0 = Part0
Motor6D.Part1 = Handle
Motor6D.C0 = OffsetCFrame
Motor6D.C1 = CFrame.new()
Motor6D.Parent = Part0
end))
table.insert(Connections, Tool.Unequipped:Connect(function()
Equipped = false
CleanupMotor()
end))
return {
SetOffset = function(NewOffset)
if Motor6D then
Motor6D.C0 = NewOffset
else
warn("[GripModule]: Attempted to set offset before Motor6D exists.")
end
end,
CleanUp = function()
for _, Conn in Connections do
Conn:Disconnect()
end
Connections = {}
CleanupMotor()
end,
}
end
Usage
Tip: Run this module on both the Client and Server for the best results.
local Tool = script.Parent
local GripModule = require(script.Parent.GripModule)
local Grip = CFrame.new(0, 0, 0) * CFrame.fromOrientation(0, 0, 0)
local NewGrip = GripModule(Tool, nil, Tool.Handle, InitialGrip) -- // Change nil to the name of Part0, e.g: "Right Arm", "Left Arm"
Notes
The default RightGrip weld is automatically destroyed.
The custom Motor6D is named "Tool6D_Server"
or "Tool6D_Client"
.
The module automatically cleans up when unequipped or reloaded.
You can update the grip offset at runtime using Grip.SetOffset(CFrame)
.
Also check out Vyntrick’s version, which achieves same result without needing to write a single line of script:
Tool 6D | Tool Animation Compatibility Layer!