A throttle script in Roblox reads a VehicleSeat's ThrottleFloat (the W/S input, from -1 to 1) and turns it into movement, usually by setting the AngularVelocity of Motor-mode HingeConstraints on the drive wheels every frame. The seat's built-in driving only works with legacy motor joints connected to it, so a car built with HingeConstraints needs this script to move.
Looking for codes for the Roblox bike game Throttle? This guide is about scripting.
What the VehicleSeat gives you
When a player sits in a VehicleSeat, it welds them in place and fills in these properties:
- ThrottleFloat: forward and backward input. 1 is forward, -1 is reverse. It goes back to 0 unless something keeps setting it.
- SteerFloat: left and right input. 1 is right, -1 is left. It also goes back to 0 unless constantly set.
- Occupant: the Humanoid sitting in the seat.
- **MaxSpeed, Torque, TurnSpeed**: the top speed, how fast the vehicle reaches it, and the turning speed. These only affect the seat's built-in driving of legacy motor joints. With the script below, speed comes from
MAX_WHEEL_SPINand the hinges'MotorMaxTorque.
Throttle vs ThrottleFloat
Older tutorials and free models use seat.Throttle and seat.Steer. Those are whole numbers (1, 0 or -1), and the current Creator Hub reference labels both as deprecated. ThrottleFloat and SteerFloat are the non-deprecated versions, so use them when you write or update a script.
Step by step: a hinge motor car
- Step 1: Put a VehicleSeat in your car model and weld the chassis parts together so the seat is part of the vehicle.
- **Step 2:** Attach each rear wheel to the chassis with a HingeConstraint and set its
ActuatorTypeto Motor. These are your drive wheels. - Step 3: Front wheels need two hinges each, because a wheel held by a Servo cannot roll. Add a small steering part for each front wheel. Connect the steering part to the chassis with a Servo HingeConstraint whose axis points up. Then connect the wheel to the steering part with a HingeConstraint set to None, so the wheel spins freely.
- **Step 4:** Set the hinge values.
MotorMaxTorquestarts at 0, so the car will not move until you raise it. Start around 20000 and adjust until it works for your car. The Servo hinges also needServoMaxTorque(for example 10000) andAngularSpeed(for example 5), or the front wheels will not turn. - Step 5: Put the hinge objects directly inside the car model with the names below (or change the names in the code), then add a Script inside the car model.
The front left corner ends up like this:
- Chassis
- FrontLeftSteer (Servo, axis up) connects to the steering part - Axle hinge (None) connects the steering part to the front left wheel
local RunService = game:GetService("RunService")
local car = script.Parent
local seat = car:WaitForChild("VehicleSeat")
local MAX_WHEEL_SPIN = 30 -- radians/sec; car speed is about this times the wheel radius
local MAX_STEER_ANGLE = 30 -- degrees
local motors = {
{hinge = car:WaitForChild("RearLeftMotor"), dir = 1},
{hinge = car:WaitForChild("RearRightMotor"), dir = -1},
}
local steering = {
car:WaitForChild("FrontLeftSteer"),
car:WaitForChild("FrontRightSteer"),
}
RunService.Heartbeat:Connect(function()
local throttle = seat.ThrottleFloat
local steer = seat.SteerFloat
for _, motor in motors do
motor.hinge.AngularVelocity = throttle * MAX_WHEEL_SPIN * motor.dir
end
for _, hinge in steering do
hinge.TargetAngle = steer * MAX_STEER_ANGLE
end
end)Here is what each part does:
- A Motor hinge tries to reach its
AngularVelocity.MotorMaxTorqueandMotorMaxAccelerationcap how hard it can push. - A Servo hinge rotates to its
TargetAngle, sosteer * MAX_STEER_ANGLEturns the front wheels left or right. - Left and right wheels usually have mirrored attachments, so one side spins backwards. That is what
diris for. If a wheel still spins the wrong way, flip itsdirbetween 1 and -1. If steering is reversed, use-steerinstead ofsteer.
If you would rather react only when input changes, seat:GetPropertyChangedSignal("ThrottleFloat") works too. Heartbeat is simpler when you want to smooth the throttle over time.
Debugging the setup
- Add
print(seat.ThrottleFloat)inside the loop to confirm the seat is getting your W/S input. - While driving in a playtest, select a motor hinge and watch its
AngularVelocityin the Properties window. - Turn on the seat's
HeadsUpDisplayto show a speed bar on screen while driving.
Common throttle problems
The seat does not move anything
This is the most common question on the DevForum. The seat's built-in driving only detects legacy motor joints connected directly to it or through a rigid connection. With HingeConstraints, sitting in the seat sets ThrottleFloat and SteerFloat and nothing else. You need a script that reads them and applies them to the motors. If you have the script and the car still sits there, check that MotorMaxTorque is not still 0.
Mobile button throttle resets to 0
Roblox's default mobile controls already set the seat's throttle, so custom pedals are optional. If you want your own gas button, do not write the seat's throttle from it, because it resets to 0 within milliseconds. Have the button send its own value to the server with a RemoteEvent.
Create a RemoteEvent named PedalThrottle in ReplicatedStorage. Put this LocalScript inside a ScreenGui that has a TextButton named GasButton:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local pedalEvent = ReplicatedStorage:WaitForChild("PedalThrottle")
local gasButton = script.Parent:WaitForChild("GasButton")
gasButton.MouseButton1Down:Connect(function()
pedalEvent:FireServer(1)
end)
gasButton.MouseButton1Up:Connect(function()
pedalEvent:FireServer(0)
end)Then add this to the car Script, above the Heartbeat connection:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local pedalEvent = ReplicatedStorage:WaitForChild("PedalThrottle")
local pedalThrottle = 0
pedalEvent.OnServerEvent:Connect(function(player, value)
if typeof(value) ~= "number" then
return
end
local occupant = seat.Occupant
if not occupant or occupant.Parent ~= player.Character then
return
end
pedalThrottle = math.clamp(value, -1, 1)
end)
seat:GetPropertyChangedSignal("Occupant"):Connect(function()
pedalThrottle = 0
end)Inside the Heartbeat function, change the throttle line so the pedal wins when it is pressed:
local throttle = if pedalThrottle ~= 0 then pedalThrottle else seat.ThrottleFloatThe car spins out of control
Very high or infinite MotorMaxTorque and MotorMaxAcceleration values can make a car spin out of control when it hits something. Set finite numbers in the HingeConstraint properties and lower them until the car stays stable.
A notch throttle for trains
Trains often use a lever that stays where you put it. Instead of reading the seat, keep your own throttle position, step it with W and S, clamp it, and send it to the server when it changes. Create a RemoteEvent named TrainThrottle in ReplicatedStorage, then put this LocalScript in StarterPlayerScripts:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local throttleEvent = ReplicatedStorage:WaitForChild("TrainThrottle")
local MIN_THROTTLE = -10
local MAX_THROTTLE = 10
local throttlePosition = 0
UserInputService.InputBegan:Connect(function(input, gameProcessed)
if gameProcessed then
return
end
local step = 0
if input.KeyCode == Enum.KeyCode.W then
step = 1
elseif input.KeyCode == Enum.KeyCode.S then
step = -1
end
if step == 0 then
return
end
throttlePosition = math.clamp(throttlePosition + step, MIN_THROTTLE, MAX_THROTTLE)
throttleEvent:FireServer(throttlePosition)
end)On the server, a Script applies the notch to a Motor hinge on the train. This one expects a model named Train in Workspace with a seat named DriverSeat and a Motor HingeConstraint named DriveMotor:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local throttleEvent = ReplicatedStorage:WaitForChild("TrainThrottle")
local train = workspace:WaitForChild("Train")
local seat = train:WaitForChild("DriverSeat")
local motor = train:WaitForChild("DriveMotor")
local MAX_THROTTLE = 10
local MAX_WHEEL_SPIN = 20 -- radians/sec, tune for your train
throttleEvent.OnServerEvent:Connect(function(player, position)
if typeof(position) ~= "number" then
return
end
local occupant = seat.Occupant
if not occupant or occupant.Parent ~= player.Character then
return
end
position = math.clamp(math.round(position), -MAX_THROTTLE, MAX_THROTTLE)
motor.AngularVelocity = position / MAX_THROTTLE * MAX_WHEEL_SPIN
end)Because the lever stays put, setting the motor once per change is enough. The DevForum notch throttle this is based on used the legacy BodyVelocity on the client only. If your train slides instead of rolling on wheels, use a LinearVelocity constraint, the modern replacement for BodyVelocity, and set its velocity in the same handler.
Throttling remotes (rate limiting)
Some people searching "throttle script" mean limiting how often something runs. Quenty's @quenty/throttle package limits how often a function runs. For RemoteEvents, a DevForum rate limiter module counts each player's requests per remote and ignores the ones over a set maximum. A simpler version is a per-player cooldown in a server Script:
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remote = ReplicatedStorage:WaitForChild("MyRemote")
local COOLDOWN = 0.2 -- seconds between accepted requests
local lastRequest = {}
remote.OnServerEvent:Connect(function(player, ...)
local now = os.clock()
if now - (lastRequest[player] or 0) < COOLDOWN then
return
end
lastRequest[player] = now
-- handle the request here
end)
Players.PlayerRemoving:Connect(function(player)
lastRequest[player] = nil
end)Do not put a cooldown like this on the pedal remote above, or a quick release can get dropped and leave the gas stuck on. For more server-side guards on RemoteEvents, see Luau patterns that hold up in a live game.
Building a car with Picoo
Hinges give you real wheel physics. A kinematic car is simpler. Picoo's vehicle recipe takes the kinematic route: it builds a car in Workspace.Car with a chassis, a VehicleSeat and four cosmetic wheels, and a LocalScript called VehicleDriver sets AssemblyLinearVelocity = LookVector * (throttle * TOP_SPEED) every frame while you are seated (TOP_SPEED defaults to 55). The generated script currently reads the seat's deprecated Throttle and Steer properties, so swap them for ThrottleFloat and SteerFloat. Press Play in Studio and sit in the VehicleSeat to drive. The generated car has no server-side speed check, so ask for one in a follow-up prompt. Picoo is premium-only: $19/mo, or $12/mo billed yearly, for 200 credits a month. See picoo.io and the drivable car guide.
Next: once your car drives, add a checkpoint system for racing.
FAQ
Why doesn't my VehicleSeat move the car?
With HingeConstraints, the seat only records W/S and A/D input as ThrottleFloat and SteerFloat. You need a script that reads those values and applies them to the AngularVelocity of Motor hinges on the wheels. Also check that MotorMaxTorque is above 0.
Is VehicleSeat.Throttle deprecated?
Yes. The Creator Hub reference labels Throttle and Steer as deprecated. Use ThrottleFloat and SteerFloat, which go from -1 to 1.
Why does my mobile throttle button reset to 0?
The seat's throttle resets to 0 unless it is set constantly. Have your button send its own value to the server with a RemoteEvent, and let the car script use that value instead of the seat's.
Why does my car spin out of control?
Your MotorMaxTorque or MotorMaxAcceleration is probably very high or infinite. Set finite values and lower them until the car stays stable.