windows-launcher/main.js sets backgroundThrottling: false on Mana's main
BrowserWindow, with a comment explaining exactly why: Mana is meant to keep
animating while the user is elsewhere, chatting, tabbed away, and Chromium's
default background throttling would turn that smooth idle drift into visible
snapping between poses. Reasonable tradeoff, set once at window creation,
and correct for the case it names, visible but unfocused.
It says nothing about a window that isn't visible at all. HIDE_MAIN_WINDOW_ AFTER_STARTUP defaults to true unless someone sets the env var to "0", so
on a normal run the main window hides itself right after startup finishes.
backgroundThrottling: false doesn't know that happened. It was set once,
at creation, and Electron has no reason to revisit it just because the
window later called .hide(). So on the one path every user actually runs,
Chromium's rendering throttle stays off for a window nobody can see: every
polling interval and the sidebar avatar canvas keep running unthrottled in
the background, by default, for everyone, not as an edge case someone
opted into.
This was investigated, not assumed. It's part of issue #398, filed as three
separate items to check rather than three bugs to fix, and item 3 was
explicitly scoped as "performance check" before anyone knew whether there
was a real issue there. There was. The fix is four new listeners next to
the existing syncOverlayVisibility ones on the same window:
mainWindow.on("hide", () => {
mainWindow.webContents.setBackgroundThrottling(true);
});
mainWindow.on("minimize", () => {
mainWindow.webContents.setBackgroundThrottling(true);
});
mainWindow.on("show", () => {
mainWindow.webContents.setBackgroundThrottling(false);
});
mainWindow.on("restore", () => {
mainWindow.webContents.setBackgroundThrottling(false);
});
Rather than one flag fixed at creation time, throttling now tracks the window's real, current visibility: on while it's actually hidden or minimized, off again the moment it's shown or restored, which is exactly when the original smoothness guarantee is supposed to apply. Same mechanism, same intent, just re-armed on the events that actually correspond to the state it's meant to describe.
The same investigation checked whether desktop-client, Mana's other
frontend, had the matching bug. It doesn't: that window has no
HIDE_MAIN_WINDOW_AFTER_STARTUP equivalent and no .hide() call anywhere
in it, so the gap this fix closes doesn't exist there to begin with. Worth
confirming before assuming symmetry between the two frontends just because
they duplicate so much else.
What made this one easy to miss is that the original code wasn't wrong
about anything it claimed. backgroundThrottling: false did exactly what
its comment said, kept the avatar smooth while visible but unfocused. The
bug wasn't in that logic; it was in a state the flag was never told about,
because nothing in a webPreferences option, set once before the window
even exists, can react to something that happens to the window afterward.
A boolean set at construction time describes the window as it was born,
not as it is now, and the two only looked like the same thing because
early on, they happened to line up.