---
title: "Task scheduler"
url: /docs/en-us/performance-optimization/microprofiler/task-scheduler
last_updated: 2026-06-11T23:11:53Z
description: "Task Scheduler coordinates tasks done in each frame as the game runs."
---

# Task scheduler

The **task scheduler** coordinates tasks done each frame as the game runs, even when it is paused. These tasks include detecting player input, animating characters, updating the physics simulation, and resuming scripts in a `Library.task.wait()` state.

While there may be multiple tasks running, the task scheduler can potentially be overloaded, especially in the following situations:

- Using a custom character rig or input scheme.
- Animating parts yourself (instead of using an `Class.Animator`).
- Depending heavily on precise physics.
- Replicating objects regularly.

## RunService

The most direct way to add frame-by-frame tasks is through the following members of `Class.RunService`:

- `Class.RunService.PreAnimation`
- `Class.RunService.PreSimulation`
- `Class.RunService.PostSimulation`
- `Class.RunService.Heartbeat`
- `Class.RunService:BindToRenderStep()`
- `Class.RunService.PreRender`

## Scheduler priority

The task scheduler categorizes and completes tasks in the following order. Some tasks may not perform work in a frame, while others may run multiple times.

* Script execution during `Class.RunService.Heartbeat` differs depending on your game's `Class.Workspace.SignalBehavior` setting. See [Deferred events](/docs/en-us/scripting/events/deferred.md).

## Best practices

To build performant games with efficiency in mind, note the following:

- **Don't connect/bind functions to the render step unless absolutely necessary.** Only tasks that must be done after input but before rendering should be done in such a way, like camera movement. For strict control over order, use `Class.RunService:BindToRenderStep()|BindToRenderStep()` instead of `Class.RunService.PreRender|PreRender`.
- **Manage physical states carefully.** `Class.RunService.PreSimulation|PreSimulation` happens **before** physics, while `Class.RunService.PostSimulation|PostSimulation` happens **after** physics. Therefore, gameplay logic that affects the physics state should be done in `Class.RunService.PreSimulation|PreSimulation`, such as setting the `Class.BasePart.Velocity|Velocity` of parts. In contrast, gameplay logic that relies on or reacts to the physics state should be handled in `Class.RunService.PostSimulation|PostSimulation`, such as reading the `Class.BasePart.Position|Position` of parts to detect when they enter defined zones.
- **Motor6D transform changes should be done on the PreSimulation event.** If you don't, `Class.Animator|Animators` will overwrite changes on the next frame. Even without an `Class.Animator`, `Class.RunService.PreSimulation|PreSimulation` is the last Luau event fired before `Class.Motor6D.Transform` is applied to part positions.