Skip to content

Profiler

The Profiler window (open it from the Windows menu) shows where each frame's time goes — scripts, physics, rendering, particles, animation — plus live renderer counters and a per-script cost table. When a map runs badly, it answers the two questions that matter: which frames are slow, and what inside them is slow.

It records only while the window is open, and costs effectively nothing while closed. Everything it shows is measured on your machine, in the running frame — no build step, no external tools.

Reading the window

The frame strip

The bar chart across the top is the last ~5 seconds of frames, newest on the right. Each bar is one frame, stacked by color:

ColorSection
blueScripts (update) — every script's onUpdate, timers, coroutines, HUD tweens
skyScripts (fixed) — every script's onFixedUpdate
redPhysics — the physics world step
yellowRender (main) — drawing the main view
peachRender (extra) — render-texture cameras, reflection probes, minimaps, the Game-view split
greenParticles
mauveAnimation — mixers and clip playback
light grayOther (our JS) — the rest of the frame callback: editor loop odds and ends, gizmos, UI work it triggers
dark grayIdle / browser — time the frame spent with none of our code running

The two dashed lines are the 60 fps (16.7ms) and 30 fps (33.3ms) budgets. A healthy map stays under the first line; bars crossing the second are visible stutter.

Click any bar to pause recording and pin that frame — the sections table switches to its exact breakdown. Press Resume to go live again.

Counters

The row under the strip is the live state of the renderer and scene:

CounterWhat it means
fps, avg, p95Frame rate, average frame time, and the time your worst 5% of frames exceed. p95 is the stutter number — a good average with a bad p95 feels worse than the reverse.
draws, trisDraw calls and triangles in the last frame. Draw calls are usually the render bottleneck — a thousand small objects cost more than ten big ones with the same triangles.
geo, tex, shadersGeometries, textures and shader programs resident on the GPU. Watch these during play: if they climb steadily while you spawn and destroy things, something is not being freed. Flat is healthy.
entities, bodiesScene entities and live physics bodies.
MB heapJavaScript heap (Chromium browsers only).

Sections and top scripts

The sections table shows the pinned (or latest) frame next to the average — a frame that looks bad against its own average tells you whether a cost is constant or a spike.

The last three rows are the ones that explain a spike nothing else accounts for:

RowWhat it means
Other (our JS)Time inside our own frame callback that no section covers. If a spike lands here, the cost is ours and the sections above narrow it down.
Idle / browserTime the frame held while none of our code ran: waiting for the display's next refresh, browser style/layout/paint, garbage collection, GPU stalls. On a healthy frame this is nearly all of it — a 7ms frame of work on a 144Hz screen still reports the full refresh interval, and the wait is what fills it. A spike here was not caused by your map.
Total frameThe whole frame, refresh to refresh. Always the sum of every row above it.

When the heap moves by more than half a megabyte across the shown frame, a heap Δ row appears. A large negative number is a garbage collection, and it is the usual answer when Idle / browser spikes on its own. Each entry in the Spikes list carries the same verdict as a tag — our JS, GC -14MB, or idle/browser — so you can tell at a glance whether a spike is yours to fix.

The top-scripts table ranks your script classes by time spent over the last second, with call counts. Every lifecycle call is attributed — onUpdate, onFixedUpdate, collision and trigger handlers, timers, coroutines — so the script eating the frame is at the top by name, whatever hook it burns the time in.

Spikes

Any frame slower than 2.5× the recent average (and over 25ms) is captured automatically: its full section breakdown plus the scripts running at that moment. The last 20 sit in the list at the bottom — click one to pin it. This is how you catch the hitch that happened three seconds ago while you were looking at the game instead of the profiler.

Pause, Clear, Export

Pause freezes the buffers while the game keeps running; Clear starts fresh; Export downloads everything — frames, sections, scripts, spikes — as JSON, which is the right thing to attach to a bug report.

Isolation toggles

The disable: row turns individual costs off while everything else keeps running — the fastest way to bisect a slow frame. Flip one, watch the strip: if the bars drop, you found your cost.

ToggleWhat it disables
main renderThe main viewport render. The view freezes on its last frame; the sim, scripts and physics keep running underneath. If frames are still slow with this on, rendering was never the problem.
editor renderOnly the editor's Scene viewport. In the Game-view split your game keeps rendering while the editor viewport's cost drops out — the cleanest way to see what your game itself costs. In edit mode it freezes the viewport; in fullscreen play and published games it does nothing (there is no editor render there).
extra rendersRTT cameras, reflection probes, minimaps, camera previews and the Game-view split — everything in Render (extra).
post-fxPost-processing; the plain view draws instead. The difference is exactly what your effects cost.
shadowsShadow-map re-rendering. Shadows freeze in place (they do not disappear), and their per-frame depth-render cost — hidden inside Render (main) — drops out.
particlesParticle simulation. Systems stay visible, frozen mid-air.
animationAnimation mixers. Poses hold where they are.

Toggles are debugging aids, not settings: they reset the moment the Profiler window closes, so a frozen viewport can never outlive the panel that explains it.

Profiling a play-test

Recording continues while you play. In fullscreen play the editor windows hide, but the profiler keeps capturing — press Stop and the reopened window holds the last 5 seconds of the session, spikes included. With the Game window open (the split view), the profiler stays on screen and updates live while you drive the player.

One thing about onFixedUpdate

Scripts (fixed) accumulates once per physics step, not per frame. When a frame runs late, the physics loop runs extra catch-up steps inside it — and a heavy onFixedUpdate runs in every one of them. So the cost of a slow fixed hook multiplies exactly when the frame is already behind, which is how a "2ms" script becomes the reason a 40ms frame happened. If Scripts (fixed) grows on exactly the frames that spike, that multiplication is your problem: move display work to onUpdate and keep onFixedUpdate to forces and physics decisions.

Profiling published games

Add ?profile=1 to the URL of any published game and the same panel docks as an overlay.

In multiplayer, scripts and physics run on the server, so those rows stay near zero — the client-side rows that matter are render, particles and animation, plus a table the editor never shows: Network, the per-second rate and size of every message kind arriving from the server. A game that stutters only in multiplayer is often not slow at all — it is drowning in messages, and this table names which ones.

A standalone build runs the full simulation locally, so ?profile=1 there gives the complete editor-style breakdown — scripts, fixed, physics and all — inside the exported game.

Reading it like a pro

  • Gray-dominated slow frames with quiet sections mean the cost is outside the game — usually garbage collection. Look for scripts allocating in onUpdate (creating arrays or objects every frame).
  • Render (main) high with few draw calls points at fragment cost: post-processing, large transparent surfaces, or too many shadow-casting lights. High draw calls with cheap frames elsewhere points at object count — merge, instance, or cull.
  • A sawtooth strip (fast-fast-slow repeating) is usually the physics catch-up pattern from the onFixedUpdate note above.
  • The geo/tex counters are your leak detector. Play for two minutes doing the thing your game does — spawning, dying, switching scenes. If the numbers ratchet up and never come back, report it with an Export attached.