Tutorials•November 8, 2026•7 min read

How to Tween a Model in Roblox with PivotTo

Move a complete Roblox Model with a CFrame driver and PivotTo. Follow an anchored arch exercise, check pivot direction, and clean up when the tween ends.

To tween a Roblox Model, animate a CFrame value and apply each update with Model:PivotTo. A Model does not expose the same Position property as a Part, so substituting a Model into a Part-position tween is not enough.

Three Parts form an arch that moves together through a shared Model pivot, driven by an interpolated CFrame value
Original Model-motion diagram, not a Studio screenshot.

Start with an anchored decorative Model

Use a small practice place with clear floor space. Insert a regular Script in ServerScriptService. The exercise below creates an arch from three anchored, non-collidable Parts and moves the whole assembly sixteen studs along world X after a three-second pause.

It is a visual exercise. It does not implement a rideable elevator, a physics-driven vehicle, or an NPC controller. Keeping collision out of this first test lets you judge whether the complete Model maintains its shape before adding gameplay constraints.

Use a CFrame driver and PivotTo

local TweenService = game:GetService("TweenService")

local model = Instance.new("Model")
model.Name = "TweenArch"
model.Parent = workspace

local pieces = {
    {Vector3.new(2, 6, 2), Vector3.new(-4, 4, 0)},
    {Vector3.new(2, 6, 2), Vector3.new(4, 4, 0)},
    {Vector3.new(10, 2, 2), Vector3.new(0, 8, 0)},
}
for index, piece in ipairs(pieces) do
    local part = Instance.new("Part")
    part.Name = "Piece" .. index
    part.Size = piece[1]
    part.Position = piece[2]
    part.Anchored = true
    part.CanCollide = false
    part.Color = Color3.fromRGB(13, 78, 241)
    part.Parent = model
end

local start = model:GetPivot()
local goal = start + Vector3.new(16, 0, 0)
local driver = Instance.new("CFrameValue")
driver.Value = start

local moveConnection = driver:GetPropertyChangedSignal("Value"):Connect(function()
    model:PivotTo(driver.Value)
end)

local tween = TweenService:Create(
    driver,
    TweenInfo.new(3, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut),
    {Value = goal}
)
tween.Completed:Once(function(state)
    moveConnection:Disconnect()
    driver:Destroy()
    print("Model tween ended:", state)
end)

task.wait(3)
tween:Play()

The PVInstance reference documents GetPivot and PivotTo, which Models inherit. The CFrameValue reference documents its Value property. TweenService interpolates that property; the connection applies the current value to the Model’s pivot.

Know which object the tween actually targets

The tween’s target is driver, not model and not one of the arch’s Parts. Its goal table therefore contains Value. The callback is the bridge from that interpolated transform to the visible assembly.

This makes a useful debugging split. If the driver’s Value changes but the Model does not move, inspect the callback and model reference. If the driver does not change, inspect tween creation, Play, the goal, and playback state. Keep both checks visible in your test notes rather than replacing the entire script after an unexplained result.

Compare the whole assembly before and after

Find TweenArch in Explorer while playtesting. Watch both posts and the beam, then compare the final arrangement with the start. Their spacing should remain unchanged while the assembly shifts to its destination.

For a second test, add a small differently colored marker Part under the same Model. This makes unintended rotation or missing descendants easier to notice. Record the expected offset before testing. Avoid adding a complex imported asset until your small Model behaves consistently.

Do I need a PrimaryPart?

This exercise deliberately uses GetPivot and PivotTo without assigning a PrimaryPart. A PrimaryPart can still be useful for defining the reference of an assembled object, but it is not a requirement for this pivot-based example. Decide where your Model’s pivot should be before designing rotations or precise alignment.

A decorative arch, a hinged door, and a vehicle may need different reference points. A centered pivot that looks reasonable for translation can be wrong for a hinge motion. Inspect the pivot in Studio and use a simple quarter-turn test before authoring a longer sequence.

The CFrame coordinate guide offers a smaller point-conversion exercise before you adapt local offsets to an entire Model.

Keep world-space and local-space goals distinct

The example adds a Vector3 offset to the starting CFrame to translate along world X while retaining its orientation. A goal built with start multiplied by CFrame.new uses an offset relative to the starting transform instead. The difference matters once the Model is rotated.

Write “move east in the world” or “move along the Model’s right direction” in your test plan. Do not treat these as interchangeable. Test with a visibly rotated copy so an accidental assumption about alignment cannot hide behind an unrotated scene.

Clean up when the movement ends

The one-time completion callback disconnects the value listener and destroys the temporary driver. It prints the ending playback state. A cancellation is an end to this attempt, not proof that the Model reached its target; the example leaves the Model where movement stopped.

For a repeated interaction, create a new driver and tween for each attempt or deliberately manage a reusable controller. Do not keep appending listeners. Give one controller ownership of the Model’s pivot so two concurrent animations do not continually reposition the same assembly.

When a second input interrupts an active movement, define the intended result first: ignore it, cancel and restart from the current pivot, or queue a later action. The correct policy depends on the object. A door and a reward display need not behave the same way.

Choose timing after the geometry works

The TweenInfo generator lets you prepare easing, direction, repeat, reverse, and delay settings. Start with one finite movement. Keep the start and goal fixed while comparing durations, then adjust easing after the endpoints are correct.

Read the TweenService introduction for a single-Part exercise and completion-state checks. For a character that must find its way around walls, use pathfinding rather than treating a direct transform tween as route navigation.

Debug a Model that breaks apart or does not move

Check the moving target and assembly separately
SymptomInspect first
Position is not a valid memberA Part goal table was given to a Model
Only one piece movesOne Part was targeted rather than applying the Model pivot
Nothing changesScript execution, Output, driver Value, callback, and Play
Direction looks wrong after rotationWorld-space versus local-space goal
Object jitters during repeated inputCompeting pivot owners and leftover listeners

Run the exercise in your own Studio practice place before adapting it to a production asset. The code was reviewed against current APIs but has not been executed here inside Studio. Documentation checked November 8, 2026.

Frequently Asked Questions

Can TweenService directly tween a Model Position property?▾

A Model does not have a Part-style Position property. This example tweens a CFrameValue and applies its updates through Model:PivotTo.

Does this example need a PrimaryPart?▾

No. It uses GetPivot and PivotTo without assigning a PrimaryPart. Choose the intended pivot before adapting it for rotation.

Does this make a rideable moving platform?▾

No. It is an anchored, non-collidable visual Model exercise. Passenger movement and physics behavior need their own implementation and tests.

Ready to build your Roblox game?

Create, edit, preview, and export your game for free. No credit card required.

Try Obby — 100% Free →