The Editor Learns To Sit Still
Leave Lux open on a patch that is not moving and it will now stop drawing it, instead of repainting the same picture 120 times a second until your battery gives up. The canvas grid stopped multiplying too: zoom all the way out and it spreads its dots instead of packing 203,000 of them into a frame you could not see anyway.

Put a patch on screen, stop touching it, and go make a coffee. Until this week Lux would spend that entire coffee break redrawing your unchanged patch at whatever rate your monitor would accept. Full egui pass, full canvas rebuild, full present, 120 times a second, producing a frame that was identical to the one before it. The laptop got warm. The fans came up. Nothing on screen moved.
The event loop was asking for a redraw on every single pass, unconditionally, for the main window and every output window. There was no test of whether anything had actually changed, because there was nothing to consult. The graph evaluator had known how to skip clean nodes for ages, so the CPU eval was already cheap. Everything downstream of it was not.
Paint when something can look different
The loop now asks a question before it asks for a frame, and the honest list of things that can make the next frame differ from the one already on screen turns out to be short:
The graph owes work. A pin was written, a node was marked dirty, or the patch contains a node that dirties itself every frame. That last one matters: if there is a Time node or a Mouse node anywhere in your patch, your patch is animating, and it runs at full rate forever. Nothing here throttles a running patch.
egui wants a repaint. Menus, tooltips, the toast that fades, the text cursor that blinks at its own pace and not the editor’s.
Input arrived. A mouse move, a keystroke, a resize. The very next iteration paints, with no clock involved. The first frame of a drag is not allowed to be late, which is the one way a feature like this becomes a bug you can feel.
The canvas is moving on its own. A wire carrying a spread scrolls its dashes; a wire with an errored node on the end shivers. Those animate on the clock, with no graph work behind them, and they keep the editor awake for as long as they are on screen. Freezing them would have been the tempting shortcut and a bad one: the keepalive frame below still advances time, so a throttled spread wire would not have frozen, it would have stepped its dashes once a second like a clock with a limp.
Something is mid-flight. A cue crossfade or a frame-sequence export both advance exactly one step per painted frame. Throttling the paint would have quietly throttled the fade and the export, which is the sort of bug that only shows up in front of an audience.
Fail all of that, and the window sleeps until the keepalive falls due one second later. An idle editor now paints about one frame a second instead of about twelve hundred.
The grid that was charging you for nothing
The other half of this is the dot grid on the canvas, which had a quieter and more entertaining problem.
The dots were spaced every 32 units of canvas, and canvas units shrink when you zoom out. Pull back to the 10% floor on a 1080p display and the viewport drags 19200 by 10800 units of canvas into view, which at one dot per 32 units works out at roughly two hundred and three thousand dots. Each one its own draw command. Every frame.
Here is the good part. At 10% zoom those dots were also scaled by the zoom, which made each one a tenth of a pixel across, covering about three percent of a pixel at fifteen percent opacity. Composite that onto the canvas and you get the canvas colour back, exactly. Not a faint grid: no grid. The zoomed-out view had never had one. Two hundred thousand draw commands a frame were being spent on a grid that provably could not put a single pixel on screen, which is either the worst or the best value for money in the codebase depending on how you look at it.
We know it composited to nothing because the new pixel test says so. Run it against the old code and it fails with “no grid dots to measure”.

So the grid now behaves the way a grid should. The stride doubles as you pull back, so the dots spread apart in canvas space instead of crowding together on screen, and the dot count stays flat at about four thousand instead of climbing into six figures. The dots hold their size on screen, so the zoomed-out grid is the same faint grid you get at 100%, rather than a field of invisible specks. When a level changes, the half-stride dots between the coarse ones fade in and out rather than half the grid vanishing at once.
The spacing you work at does not move. At 100% zoom and closer, the level of detail is a no-op by construction, all the way to the 10x ceiling: its minimum spacing is the grid’s own spacing, so there is nothing left to double. This was a fix for the overview, and it does not get to re-lay anybody’s desk.
The dots themselves did change size, and everywhere, so you should hear it from us rather than notice it. They used to be drawn in canvas space, which means they rode the zoom like everything else: a 1 px dot at 100%, a 10 px blob at the zoom ceiling, a 0.1 px speck at the floor. That last one is why the old zoomed-out grid was not faint but literally absent, and it is not a size anyone was choosing on purpose. A dot is now 1 px on screen at every zoom. Punch all the way in and the dots stay dots instead of swelling into a polka-dot bedspread behind your patch.
The fade stops at half strength rather than trailing off to nothing, and that is not a taste call. The grid is deliberately a whisper: at full opacity a dot moves a pixel by less than two parts in 255. Halve that and it composites to the background exactly, so the tail of a fade would have been three invisible draw commands for every visible one. The dots wink out at the point they stop being anything at all.
What is actually gated
The redraw gate is a state machine with a test for every transition: a still patch sleeps, a Time node keeps it awake forever, a pin edit wakes it on the next iteration with no clock advance, a window event does the same, and a simulated ten seconds of vsyncs on a frozen patch produces ten frames instead of twelve hundred. The grid is gated on the draw commands it emits, at every zoom from the floor to 4x, and on the actual pixels vello puts on the canvas, which is the test that caught the invisible-fade bug that the command count was perfectly happy with.
Your fans get the coffee break off.