How Myaku works
Frames
Myaku wraps requestAnimationFrame. The first callback with a new timestamp starts a frame. A frame's interval runs from its start to the next frame's start, which is the gap the player sees. Its CPU time is how long its frame callbacks ran.
GPU work
Myaku wraps getContext on canvases and offscreen canvases. On each WebGL context it counts draw* calls, texture and buffer uploads with their byte sizes, and shader compiles and program links. On 2D contexts it counts drawing calls. Contexts created before myaku starts aren't counted, which is why it should load first.
Attribution
Chromium reports Long Animation Frames: for any frame over 50 ms, which scripts ran, from which file and function, and how long style and layout took. Myaku keeps these entries and matches them to frames by time.
Because myaku's wrapper calls your frame callbacks, the browser attributes them to myaku. So when a callback is first registered, myaku records where it came from (one stack capture per distinct callback, cached), and points the verdict there instead.
Garbage collection
In Chromium, performance.memory gives the heap size. A drop of a megabyte or more between frames, while the game's code was idle, is a strong sign of a collection.
Cost
Frames live in fixed-size typed arrays, so recording a frame doesn't allocate, and myaku doesn't cause the GC it's looking for. Its own time per frame is measured and reported as overheadMs. Verdicts are only computed when you ask for them.
The first 10 frames (scripts compiling, assets decoding) are warm-up and aren't counted as play.
What myaku can't see
- GPU time. A GPU-bound frame shows as
unknown, with a hint when draw calls are high. - Inside an engine's loop, without spans.
- Script locations outside Chromium. Other browsers still get frame times, counts and spans.
- Workers. Only the main thread is profiled.
The design is recorded in ADR 0011.