Open Bug 2020360 Opened 1 month ago Updated 7 days ago

WebSerial lags when drawing line in Flipper Zero paint app

Categories

(Core :: DOM: Device Interfaces, defect, P2)

defect

Tracking

()

People

(Reporter: gstoll, Unassigned)

References

(Blocks 1 open bug)

Details

Navigate to https://lab.flipper.net
Click Connect
Choose “Flipper Aik0r0” and click “Allow”
Switch to the Paint section
Using the default Pencil tool, click the mouse down, move the mouse to draw a line, and release the mouse button

While the mouse is down, the line will not draw, which is wrong. After releasing the mouse button, the line will draw correctly in the web browser, but the line will not draw on the Flipper Zero. (the next time you draw something, the Flipper Zero will update correctly)

Severity: -- → S3
No longer depends on: 2016736
Priority: -- → P2
See Also: → 2016736

I think what's happening is that the Flipper Lab source code is inefficient, causing it to tie up the main thread of its content process and delaying the serial calls. Here's what Claude says:

Found it! The culprit is in the Flipper Lab app's imageDataToXBM() function in shared/lib/utils/pixeleditor/xbm.js.

The Bug

On every sendFrame() (throttled to 100ms during drawing), imageDataToXBM() converts the 128x64 pixel canvas to XBM
format. The inner loop is very inefficient:

for (let c4 = 0; c4 < 256; c4++) {
if (JSON.stringify(xbmValues[String(c4)]) === JSON.stringify(hexBits)) {
xbmBytes.push(c4)
}
}

For each of the 1024 bytes in the XBM output, it brute-force scans all 256 possible byte values, comparing arrays via
JSON.stringify(). That's ~524,288 JSON.stringify() calls per frame, synchronously on the main thread.

Why Firefox Is Worse Than Chrome

Both browsers run this code, but SpiderMonkey's JSON.stringify() on small arrays is apparently significantly
slower than V8's, pushing the total time from "noticeable but tolerable" in Chrome to ~2 seconds in Firefox.

The Call Chain

mousemove → handleInteraction() → sendFrame() → imageDataToXBM(imageData) → 524K × JSON.stringify() → main thread
blocked → no mousemove events delivered

The Fix

The entire 256-iteration JSON.stringify comparison loop can be replaced with direct bit manipulation:

let byte = 0;
for (const bit of hexBits) {
byte |= (1 << bit);
}
xbmBytes.push(byte);

This eliminates all JSON.stringify calls entirely, turning an O(256) linear scan with serialization into O(8) bitwise
OR. The xbm-values.js lookup table becomes unnecessary.

This is a bug in the Flipper Lab app, not in Firefox or WebSerial. That said, it might be worth filing a SpiderMonkey
bug about JSON.stringify performance on small arrays since the performance gap with V8 is significant enough to cause
real-world breakage.

Actually, this doesn't happen on the Mac, and the latest changes we have to more efficiently use the serial thread on Windows seem to make this go away. So once bug 2010930, I'll check this one more time and we can probably close this.

Blocks: 2026743

This issue is back but it only seems to happen on Windows. Given that this is a pretty unusual app, I think we can be OK shipping with this.

No longer blocks: 2026743
You need to log in before you can comment on or make changes to this bug.