Skip to content

Introducing Myaku ​

Every web game developer knows the moment. You're playing your game, and it hitches. A tiny freeze, a skipped beat, then it's smooth again. You open DevTools, start recording, play for a minute, and it doesn't happen again. Or it does, and you're looking at a flame chart with ten thousand bars, wondering which one was the problem.

Most hitches never get explained. They get blamed on "the browser", or on a slow machine, and players live with them.

Myaku (脈, Japanese for "pulse") is our answer: a frame profiler built for games, not pages.

ts
import "myaku/auto";

That one line, first in your entry file, puts a small frame graph in the corner of your game. Red spikes are hitches. Hover to hold the graph still, click a spike, and myaku tells you what happened:

Frame 1,204 took 79 ms (budget 9.1 ms). 80 ms was script: spawnWave in src/enemies.ts:88. This ran inside the game loop. Profile the function in the Performance panel, or spread the work over several frames.

Verdicts, not flame charts ​

Myaku doesn't ask you to read a profile. For every slow frame it gives a verdict: a cause, how confident it is, and what to try. The causes are the ones that actually make games stutter:

  • Your script, named by function and file when the browser reports it.
  • Garbage collection: the heap dropped by megabytes while your code sat idle.
  • GPU uploads: textures or buffers created mid-play, counted in bytes.
  • Shader compiles the first time an effect is used.
  • Layout: DOM UI over the canvas, recalculated every frame.
  • The browser itself: a hidden tab, or a covered window being throttled.

And when nothing explains it, myaku says so, with low confidence, instead of guessing.

Always on ​

A profiler you have to start is a profiler that misses the hitch. Myaku keeps the last ten seconds of frames, all the time. When you feel a stutter, it's already recorded, and the overlay shows it.

That only works if the profiler is cheap. Myaku records frames into fixed-size typed arrays, so it doesn't allocate per frame and doesn't cause the garbage collection it's trying to find. It measures its own cost and reports it: on our test games, about 0.05 ms a frame.

Any engine ​

Myaku watches the game from the outside: requestAnimationFrame, the canvas context, and what the browser reports. So it works with Phaser, PixiJS, three.js, KAPLAY, Excalibur, a loop you wrote yourself, and debuff, with no changes to the game.

We tested it on Phaser's Runner example. We made a function busy itself for 80 ms in one frame, and myaku named it. We created four 2048×2048 textures in one frame, and it reported 64 MB uploaded to the GPU, with a "high" confidence verdict. We hid the browser window behind others, and instead of blaming the game for two frames a second, it said the browser was throttling.

When an engine owns the loop, the slow callback is the engine's step function, so attribution names the engine's file. Spans let you see inside it:

ts
profiler.span("physics", () => world.step(dt));

Now verdicts read "Spans: physics 22 ms, render 6.0 ms", and the report shows each system's average cost per frame.

Built for tools and agents ​

Everything myaku knows is plain JSON. profiler.report() gives frame-time percentiles (p50, p95 and p99, because averages hide stutter), hitches per minute, causes, the worst hitches explained, and spans. It's exposed as window.__MYAKU__, so Playwright can read it after a scripted playthrough.

In debuff projects, debuff dev starts myaku automatically, with the game's update and render as spans. Coding agents get it through the MCP server's game_profile tool, and the explain-stutter prompt walks them through the verdicts to the code.

Part of a bigger idea ​

Myaku is the second standalone tool from the debuff project, after Keiko, our logic-testing framework. Both follow the same rule: solve one real problem in the web game stack properly, work with any engine, and integrate deeply with debuff without depending on it.

Get started with Myaku →