The Performance Profiler
What is it?
Introduced in Babylon.js 5.0, The Performance Profiler is a fantastic visual performance debugging tool. As you develop your Babylon experience, this new tool can help you easily identify performance issues and hiccups in your scene.

Available in the Statistics tab of the inspector, it allows you to visually see data charted over time rather than seeing the latest number at any one moment. The data is normalized on the displayed range, meaning the smallest value corresponds to the bottom y-position on the graph, while the highest value corresponds to the top y-position.
The Performance Profiler can collect and display 3 types of data: 1) all the data under the statistics tab, 2) user-defined data, and 3) user-defined events through code.
It has two modes for recording data:
- Real-time graph mode.
- Headless recording mode.
Use Case
You would want to use this to debug any performance issues in your scene and figure out a potential cause. For example, you may notice that FPS drops whenever you do a specific action in the scene, and that draws increase simultaneously.
How to use it

There are four buttons to choose from on this screen:
- Open Realtime Perf Viewer
- Load Perf viewer using CSV
- Export Perf to CSV
- Begin Recording
"Open Realtime Perf Viewer" will open the performance viewer popout in real-time graph mode. While "Load Perf viewer using CSV" will open the performance viewer popout in a view-only mode.

The left sidebar in the popout holds the activated collection types. It allows the user to toggle visibility and change color through the checkbox and color picker, respectively.

The center contains the graph, and you can hover at a particular timestamp to view all data values at the timestamp.

You can zoom in and out using the mouse wheel.
You can pan the graph by dragging while clicking, taking the graph out of the live data stream.
You can hover over a single line outside the live data stream to focus on it, fading out the other lines.
A horizontal line shows where that point is relative to every other point on the focused line.

To return to the live data stream, you can either catch up to the live data stream or conveniently click the "Return" button.
"Begin Recording" will only begin recording in headless recording mode. This button also will allow you to stop recording when it changes the text to "Stop recording."

"Export Perf to CSV" allows you to export the performance recording to a CSV. The CSV export provides for sharing of performance data and also importing at a later time.
"Begin Recording" and "Export Perf to CSV" are helpful for the mobile experience, as the popout will not work as intended in real-time graph mode while on mobile. You will have to export the recording and import it on a PC. However, these can also be used on the PC outright.
Headless recording mode vs. Real-time graph mode.
Real-time graph mode opens up a popout and begins recording, while headless recording mode begins recording without the popout.
Therefore, real-time graph mode will allow you to see a graph updating once per frame with performance data.
Headless recording mode will have a lower performance impact and not rely on the popout, useful for mobile.
Collection Strategies
A collection strategy is just a strategy defining how to collect the data and do clean up appropriately.
There are many collection strategies already defined for you in the PerfCollectionStrategy class.
We currently support these out of the box:
Id | Strategy |
---|---|
Id FPS | Strategy FpsStrategy |
Id Total Meshes | Strategy TotalMeshesStrategy |
Id Active Meshes | Strategy ActiveMeshesStrategy |
Id Active Indices | Strategy ActiveIndicesStrategy |
Id Active Faces | Strategy ActiveFacesStrategy |
Id Active Bones | Strategy ActiveBonesStrategy |
Id Active Particles | Strategy ActiveParticlesStrategy |
Id Draw Calls | Strategy DrawCallsStrategy |
Id Total Lights | Strategy TotalLightsStrategy |
Id Total Vertices | Strategy TotalVerticesStrategy |
Id Total Materials | Strategy TotalMaterialsStrategy |
Id Total Textures | Strategy TotalTexturesStrategy |
Id Absolute FPS | Strategy AbsoluteFpsStrategy |
Id Meshes Selection | Strategy MeshesSelectionStrategy |
Id Render Targets | Strategy RenderTargetsStrategy |
Id Particles | Strategy ParticlesStrategy |
Id Sprites | Strategy SpritesStrategy |
Id Animations | Strategy AnimationsStrategy |
Id Physics | Strategy PhysicsStrategy |
Id Render | Strategy RenderStrategy |
Id Frame Total | Strategy FrameTotalStrategy |
Id Inter Frame | Strategy InterFrameStrategy |
Id GPU Frame Time | Strategy GpuFrameTimeStrategy |
Id CPU Utilization | Strategy CpuStrategy |
The CPU strategy is more experimental as it uses the experimental computer pressure API. This strategy may not work as intended for all users. You might have to enable experimental web features in chrome settings to enable this. This strategy allows you to know the utilization percentage of the CPU.
Adding a custom collection strategy
You may have custom data that you want to track that we do not support outside of the box. Don't worry, we've made this very easy to do.
To start, you must define a function that returns an object like so:
// This variable would be updated somewhere else.var someVariableToTrack;
const someStrategyCallback = (scene) => { return { /** * The id of the strategy. */ id: "some strategy id", /** * Function which gets the data for the strategy. */ getData: () => someVariableToTrack, /** * Function which does any necessary clean-up. Called when performanceViewerCollector.dispose() is called. */ dispose: () => { // do any clean up here. } }};
When added to the performance collector, the collector passes the scene object as a parameter to the function (you can omit the scene parameter if you would like). The function allows for any initialization of observers and other resources.
The id parameter is the name of the strategy. Make sure to have a unique id, or else there may be unintended side effects. Also, if your id contains the character "@", the character will be removed, since it is already used when exporting data. The left sidebar will display the id defined. The getData function in the object must return a number that cannot be NaN. It is called once per frame. The dispose function in the object should do any clean-up of resources created in the initialization part of the function.
Here is an example of how we keep track of draw calls in the PerfCollectionStrategy class:
public static DrawCallsStrategy(): PerfStrategyInitialization { return (scene) => { let drawCalls = 0; const onBeforeAnimationsObserver = scene.onBeforeAnimationsObservable.add(() => { scene.getEngine()._drawCalls.fetchNewFrame(); });
const onAfterRenderObserver = scene.onAfterRenderObservable.add(() => { drawCalls = scene.getEngine()._drawCalls.current; });
return { id: "draw calls", getData: () => drawCalls, dispose: () => { scene.onBeforeAnimationsObservable.remove(onBeforeAnimationsObserver); scene.onAfterRenderObservable.remove(onAfterRenderObserver); }, }; };}
In the function, we create two observers and update the value of draw calls in the onAfterRenderObservable. We return the value of draw calls in getData. We remove all observers inside the dispose function.
Now that you have a collection strategy, you have to add it to the performance collector. To do so, you also have to specify a category for your strategy to be displayed in the left bar (you can leave it empty to display on top), and if that strategy is hidden or visible by default.
First, get the performance collector like so:
const perfCollector = scene.getPerfCollector();
Then add the strategy we defined earlier to the performance collector:
const someStrategy = { strategyCallback: someStrategyCallback, category: "My custom category", hidden: false}perfCollector.addCollectionStrategies(someStrategy);

Registering a custom event.
An event is a particular type of strategy that can measure the number of times it was called in a frame or track a named custom value (related to an event usually) once per frame. To register an event you would do the following:
const perfCollector = scene.getPerfCollector();const event = perfCollector.registerEvent("someEvent");
The register event function returns an object containing the event passed in:
{ /** * The name of the event. */ name: "",}
If you want to count the number of times an event was called, you would want the following somewhere in the code:
perfCollector.sendEvent(event);
If you want to track a custom value once per frame related to an event, try this:
// somewhere in the code you want thisevent.value = someValue;
// followed by this somewhere in the codeperfCollector.sendEvent(event);
Note: You shouldn't set the value property to undefined after selecting a value for it. Doing so may result in unexpected behavior (this will switch between counting the number of occurrences and tracking a custom value related to an event).
Custom Performance Profiler event with value