Spans and engines
Myaku sees the game loop from the outside: the frame callbacks, the canvas context and what the browser reports. It doesn't know that your loop runs physics, then AI, then rendering. Spans tell it.
import { myaku } from "myaku";
const profiler = myaku();
function update(dt: number) {
profiler.span("physics", () => world.step(dt));
profiler.span("ai", () => enemies.think(dt));
}For work that crosses callbacks, use begin:
const end = profiler.begin("pathfinding");
await findPath(start, goal);
end();Spans show up in each frame (frame.spans), in verdicts ("Spans: physics 22 ms, ai 6.0 ms") and in the report, as average milliseconds per frame. Myaku keeps up to 16 span names.
Engine spans, automatically
Myaku already adds update and render spans for the engine's own loop, so a slow frame says whether game logic or drawing took the time:
| Engine | How | Spans |
|---|---|---|
| PixiJS 8 | Automatic, through Pixi's devtools hook | update (your ticker listeners), render |
| three.js | Automatic, through three's devtools hook | render |
| Phaser 3, or Phaser from a script tag | Automatic, through the global Phaser | update, update:<scene key>, render |
| Phaser 4 (ES modules), or anything else | adapt(profiler, game) | Same as above |
| debuff | debuff dev | update, render |
Phaser 4's ES module build has no global, so hand it the game:
import { adapt, myaku } from "myaku";
const game = new Phaser.Game(config);
adapt(myaku(), game);Spans can nest: update:level is part of update. Turn engine spans off with myaku({ engines: false }).
Add your own spans inside them to go further.
Engine guides: Phaser · PixiJS · three.js
Your own spans in Phaser
Phaser owns the loop, so wrap your scenes' update:
class Level extends Phaser.Scene {
update(time: number, delta: number) {
profiler.span("level", () => this.tick(delta));
}
}Your own spans in PixiJS
Wrap individual ticker callbacks:
app.ticker.add((ticker) => profiler.span("game", () => game.update(ticker.deltaMS)));Your own spans in three.js
renderer.setAnimationLoop(() => {
profiler.span("update", () => update());
profiler.span("render", () => renderer.render(scene, camera));
});