agentleFS
EL4CTEOEL4CTEO/roblox-skills/skills/roblox-performance/SKILL.md

roblox-performance

Skill92 starsChanged 47 days agoMITView on GitHub

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.

3 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

3 rules. 0 are common, 0 are distinctive, and 3 are very specific to this project.

  • 0

    Common

    Most of the 3 edited versions we read keep these.

  • 0

    Distinctive

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use deltaTime from Heartbeat/PreSimulation rather than task.wait() loops for per-frame work.

    line 72
  2. Very specific:

    Avoid creating closures/tables per frame in hot loops.

    line 73
  3. Very specific:

    Avoid setting CFrame of unanchored parts every frame.

    line 95

“Common” is measured: kept by at least 6 in 10 of the 3 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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

ToolOpenUse for
Developer ConsoleF9 (in game)Errors, server/client memory by category, network, script activity. Works in live servers.
MicroProfilerCtrl+Alt+F6 (⌘⌥F6); Dev Console for serverPer-frame timing; spikes; dump a frame for detail.
Script ProfilerStudio / Dev ConsoleWhich functions consume Luau time (sampling).
Luau heap snapshotsDev Console → Memory → Luau HeapWhat tables/closures/instances are held and by what (leaks).
Scene AnalysisStudioScript memory, unparented instances (leaks), instance composition, audio/animation memory, triangles. Also SceneAnalysisService via Studio MCP.
Performance Stats / Debug StatsCtrl+Alt+F7 / Shift+Ctrl+F1–F5Live CPU/GPU/memory/network overlays.
Network SimulatorStudio Device Simulator toolbarTest with latency, jitter, packet loss (3G/4G/Wi-Fi profiles).
Performance dashboardCreator HubLive aggregate FPS, server heartbeat, memory, crash rates by device.

Label your own code in the MicroProfiler and memory views:

--!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.
  • Server OOM: game.ServerLowMemoryWarning fires (server only, live servers and Team Test, not Studio playtests) when memory leaves normal levels, then every 60 s while it stays high. Use it to free memory fast (destroy debris/effects, pause spawning, unload empty map areas) and log it; it frees nothing itself.
--!strict
game.ServerLowMemoryWarning:Connect(function()
	warn("Server memory high: cleaning up")
	-- e.g. destroy temporary effects, pause NPC/item spawners until memory recovers
end)

Most common problems → fixes

SymptomUsual causeFix
Memory grows over timeLeaked connections, tables keyed by players/instances, destroyed-but-referenced instances, unparented instancesDisconnect/clean up per player & per character; clear tables on PlayerRemoving; use Scene Analysis "unparented instances" and heap snapshots.
Server lag with many playersPer-player loops each frame, while true do task.wait() end loops, heavy remote handlers, physics of many unanchored partsEvent-driven code, one scheduler loop, throttled think rates, anchor static parts, server-own fewer assemblies.
Client FPS dropsToo many parts/draw calls, shadows from many lights, transparency/particles overdraw, per-frame UI updatesMerge/instance meshes, CastShadow = false on small parts, fewer shadowed lights, limit particles, update UI on change only.
Spikes every N secondsAutosave/GC bursts, synchronized loops across players, big Clone()sStagger with random offsets; pool objects; spread work across frames.
High ping / rubber bandingRemote spam, large payloads, replicating many property changesBatch, delta-compress, throttle; tween on clients; see roblox-networking.
Long join timesHuge place, many unique assets, no streamingStreaming, 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; MeshParts 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:

--!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.

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).

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.