Performance diagnosis and optimization - MicroProfiler, Script Profiler, Developer Console, Scene Analysis, Luau heap and memory categories, memory leaks, network/replication cost, rendering and physics cost, streaming, native codegen, Parallel Luau with Actors. Use when a game lags, stutters, drops FPS, crashes on mobile, has high ping, or before optimizing hot code.
Installs into .claude/skills of the current project.
Are you the author of Roblox Performance?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/el4cteo-roblox-performance)
---
name: roblox-performance
description: Performance diagnosis and optimization - MicroProfiler, Script Profiler, Developer Console, Scene Analysis, Luau heap and memory categories, memory leaks, network/replication cost, rendering and physics cost, streaming, native codegen, Parallel Luau with Actors. Use when a game lags, stutters, drops FPS, crashes on mobile, has high ping, or before optimizing hot code.
---
# Roblox performance
**Measure before optimizing.** Identify whether the bottleneck is server compute, client compute
(CPU/GPU), memory, network, or load time, then use the matching tool.
## Tools
| Tool | Open | Use for |
| --- | --- | --- |
| Developer Console | F9 (in game) | Errors, server/client memory by category, network, script activity. Works in live servers. |
| MicroProfiler | Ctrl+Alt+F6 (⌘⌥F6); Dev Console for server | Per-frame timing; spikes; dump a frame for detail. |
| Script Profiler | Studio / Dev Console | Which functions consume Luau time (sampling). |
| Luau heap snapshots | Dev Console → Memory → Luau Heap | What tables/closures/instances are held and by what (leaks). |
| Scene Analysis | Studio | Script memory, unparented instances (leaks), instance composition, audio/animation memory, triangles. Also `SceneAnalysisService` via Studio MCP. |
| Performance Stats / Debug Stats | Ctrl+Alt+F7 / Shift+Ctrl+F1–F5 | Live CPU/GPU/memory/network overlays. |
| Network Simulation | Studio (Alt+S) | Test with latency, jitter, packet loss. |
| Performance dashboard | Creator Hub | Live aggregate FPS, server heartbeat, memory, crash rates by device. |
Label your own code in the MicroProfiler and memory views:
```luau
--!strict
local RunService = game:GetService("RunService")
debug.setmemorycategory("EnemyAI") -- memory allocated by this thread is attributed to "EnemyAI"
RunService.Heartbeat:Connect(function(_deltaTime)
debug.profilebegin("EnemyAI.think") -- shows as a labeled bar in the MicroProfiler
-- ... work ...
debug.profileend()
end)
```
## Targets
- Client: 60 FPS on mid-range phones (≈16.6 ms/frame); many players are on low-end Android.
- Server: heartbeat near 60 Hz; `Stats` (`workspace:GetRealPhysicsFPS()`, Dev Console Server Stats).
- Memory: mobile clients crash from OOM first. Keep client memory well under ~1–1.5 GB on low-end
devices; watch `PlaceMemory`, `LuaHeap`, `GraphicsTexture`, `Sounds`, `Animation` categories.
## Most common problems → fixes
| Symptom | Usual cause | Fix |
| --- | --- | --- |
| Memory grows over time | Leaked connections, tables keyed by players/instances, destroyed-but-referenced instances, unparented instances | Disconnect/clean up per player & per character; clear tables on `PlayerRemoving`; use Scene Analysis "unparented instances" and heap snapshots. |
| Server lag with many players | Per-player loops each frame, `while true do task.wait() end` loops, heavy remote handlers, physics of many unanchored parts | Event-driven code, one scheduler loop, throttled think rates, anchor static parts, server-own fewer assemblies. |
| Client FPS drops | Too many parts/draw calls, shadows from many lights, transparency/particles overdraw, per-frame UI updates | Merge/instance meshes, `CastShadow = false` on small parts, fewer shadowed lights, limit particles, update UI on change only. |
| Spikes every N seconds | Autosave/GC bursts, synchronized loops across players, big `Clone()`s | Stagger with random offsets; pool objects; spread work across frames. |
| High ping / rubber banding | Remote spam, large payloads, replicating many property changes | Batch, delta-compress, throttle; tween on clients; see `roblox-networking`. |
| Long join times | Huge place, many unique assets, no streaming | Streaming, SLIM LOD, fewer unique meshes/textures, `ContentProvider:PreloadAsync` only for critical assets. |
## Luau hot paths
- Cache services, instances, and `RaycastParams` outside loops; avoid `FindFirstChild`/`GetDescendants`
per frame. Replace `GetDescendants` scans with tags (`CollectionService`) or maintained sets.
- Use `deltaTime` from `Heartbeat`/`PreSimulation` rather than `task.wait()` loops for per-frame work.
- Avoid creating closures/tables per frame in hot loops; reuse buffers with `table.clear`.
- `--!native` (whole script) or `@native` (per function) for math-heavy code; typed parameters help
the native compiler. Don't blanket-enable: compile time and a global native-code memory limit.
- `--!optimize 2` is how Roblox compiles live games already; don't rely on Studio timings for micro-benchmarks.
## Rendering and assets
- Parts: fewer, larger parts; `MeshPart`s with shared `MeshId` are instanced (cheap); unions (`PartOperation`)
can be expensive — check triangle counts in Scene Analysis.
- `RenderFidelity = Automatic`/`Performance` for detailed meshes; `CollisionFidelity = Box`/`Hull` for
decorative meshes; `CanCollide/CanQuery/CanTouch = false` for pure decoration.
- Lights: limit shadow-casting `PointLight`/`SpotLight`/`SurfaceLight`; limit `ParticleEmitter.Rate`
and particle size (overdraw); `Beam`/`Trail` counts.
- Textures/images: reuse, keep resolutions reasonable (1024² max for most); `SurfaceAppearance` sparingly.
- Enable streaming (`roblox-architecture`), `Model.LevelOfDetail = SLIM` for static models, and let
`StreamOutBehavior = Opportunistic` free memory.
## Physics
- Anchor everything that doesn't need to move. Welded decorative parts on moving assemblies add cost.
- Fewer, simpler collisions (`CollisionFidelity`, collision groups to skip pairs); `CanTouch = false`
where `Touched` isn't needed.
- Avoid setting `CFrame` of unanchored parts every frame; use constraints or anchor + CFrame.
- `workspace:BulkMoveTo(parts, cframes, Enum.BulkMoveMode.FireCFrameChanged)` to move many anchored parts.
- Adaptive timestepping (`PhysicsSteppingMethod = Adaptive`) reduces physics CPU.
## Parallel Luau
Scripts under different `Actor` instances can run in parallel:
```luau
--!strict
-- Script parented under an Actor (one Actor per logical unit, e.g. per NPC group or chunk).
local RunService = game:GetService("RunService")
local results: { number } = {}
RunService.Heartbeat:ConnectParallel(function(_deltaTime)
-- Parallel phase: read-only access to most instances, pure computation, thread-safe APIs (raycasts).
for i = 1, 100 do
results[i] = math.noise(i / 10, os.clock())
end
task.synchronize()
-- Serial phase: write to instances, fire events, call thread-unsafe APIs.
end)
```
- `task.desynchronize()` / `task.synchronize()` switch phases; `ConnectParallel` starts in parallel.
- Scripts in the **same** Actor run serially relative to each other: split work across many Actors.
- Can't `require` or modify most instances while desynchronized. Communicate with `Actor:SendMessage` /
`BindToMessageParallel` or `SharedTable` (`SharedTableRegistry`).
- Good fits: raycast validation, procedural generation, pathfinding-adjacent math, large simulations.
Bad fits: tiny tasks (sync overhead), instance-heavy work.
Profiling walkthrough and memory-leak hunting: [references/profiling.md](references/profiling.md).
## Related skills
`roblox-architecture` (streaming), `roblox-networking` (bandwidth), `roblox-physics`, `roblox-npc-ai`,
`roblox-studio-mcp` (Roblox's `rbx-perf-profiling`/scene analysis via MCP).