From 67904dfae697aaa88d524640ca14dcd6f986d53b Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Fri, 17 Apr 2026 15:28:46 +0200 Subject: [PATCH 1/3] better packet queuing / pacing for custom palette live preview much fewer packets get dropped on larger setups --- wled00/data/cpal/cpal.htm | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/wled00/data/cpal/cpal.htm b/wled00/data/cpal/cpal.htm index 4e41757a94..aa68cc1c68 100644 --- a/wled00/data/cpal/cpal.htm +++ b/wled00/data/cpal/cpal.htm @@ -71,6 +71,7 @@ let copyColor = '#000'; let ws = null; let maxCol; // max colors to send out in one chunk, ESP8266 is limited to ~50 (500 bytes), ESP32 can do ~128 (1340 bytes) + let _applySeq = 0; // incremented each time applyLED fires; used to cancel stale in-flight previews // load external resources in sequence to avoid 503 errors if heap is low, repeats indefinitely until loaded (function loadFiles() { @@ -621,21 +622,20 @@ async function requestJson(cmd) { - if (ws && ws.readyState == 1) { + if (ws && ws.readyState == 1 && ws.bufferedAmount < 32768) { try { ws.send(JSON.stringify(cmd)); + await new Promise(r => setTimeout(r, 15)); // short delay to give ESP time to process (fewer packets dropped) return 1; } catch (e) {} } + // HTTP fallback if (!window._httpQueue) { window._httpQueue = []; window._httpRun = 0; } - if (_httpQueue.length >= 5) { - return Promise.resolve(-1); // reject if too many queued requests - } - + if (_httpQueue.length >= 5) return -1; // queue full; applyLED cancels stale queues before sending return new Promise(resolve => { _httpQueue.push({ cmd, resolve }); (async function run() { @@ -650,7 +650,7 @@ cache: 'no-store' }); } catch (e) {} - await new Promise(r => setTimeout(r, 120)); + await new Promise(r => setTimeout(r, 120)); // delay between requests (go slow, this is the http fallback if WS fails) q.resolve(0); } _httpRun = 0; @@ -662,8 +662,12 @@ async function applyLED() { if (!palCache.length) return; + const seq = ++_applySeq; + // discard pending HTTP chunks from any previous preview so stale data doesn't drain slowly + if (window._httpQueue) _httpQueue.length = 0; try { let st = await (await fetch(getURL('/json/state'), { cache: 'no-store' })).json(); + if (seq !== _applySeq) return; // superseded by a newer preview request if (!st.seg || !st.seg.length) return; // get selected segments, use main segment if none selected @@ -680,6 +684,7 @@ arr.push(palCache[len > 1 ? Math.round(i * 255 / (len - 1)) : 0]); // send colors in chunks for (let j = 0; j < arr.length; j += maxCol) { + if (seq !== _applySeq) return; // superseded mid-send let chunk = [s.start + j, ...arr.slice(j, j + maxCol)]; await requestJson({ seg: { id: s.id, i: chunk } }); } From 1cacd13efe5b186f6c4f00324d3b17e6d32be639 Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Fri, 17 Apr 2026 15:44:39 +0200 Subject: [PATCH 2/3] fix leak --- wled00/data/cpal/cpal.htm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/wled00/data/cpal/cpal.htm b/wled00/data/cpal/cpal.htm index aa68cc1c68..84bdadd1b5 100644 --- a/wled00/data/cpal/cpal.htm +++ b/wled00/data/cpal/cpal.htm @@ -664,7 +664,9 @@ if (!palCache.length) return; const seq = ++_applySeq; // discard pending HTTP chunks from any previous preview so stale data doesn't drain slowly - if (window._httpQueue) _httpQueue.length = 0; + if (window._httpQueue) { + while (_httpQueue.length) _httpQueue.shift().resolve(-1); // resolve dropped entries so their awaiters can observe the seq change and exit + } try { let st = await (await fetch(getURL('/json/state'), { cache: 'no-store' })).json(); if (seq !== _applySeq) return; // superseded by a newer preview request From c32858eb79edbd58362ad446a0a9213581a4e19c Mon Sep 17 00:00:00 2001 From: Damian Schneider Date: Sat, 18 Apr 2026 16:06:02 +0200 Subject: [PATCH 3/3] remove window prefix from variables --- wled00/data/cpal/cpal.htm | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/wled00/data/cpal/cpal.htm b/wled00/data/cpal/cpal.htm index 84bdadd1b5..7c3ca44ac7 100644 --- a/wled00/data/cpal/cpal.htm +++ b/wled00/data/cpal/cpal.htm @@ -72,6 +72,7 @@ let ws = null; let maxCol; // max colors to send out in one chunk, ESP8266 is limited to ~50 (500 bytes), ESP32 can do ~128 (1340 bytes) let _applySeq = 0; // incremented each time applyLED fires; used to cancel stale in-flight previews + let _httpQueue = [], _httpRun = 0; // load external resources in sequence to avoid 503 errors if heap is low, repeats indefinitely until loaded (function loadFiles() { @@ -631,10 +632,6 @@ } // HTTP fallback - if (!window._httpQueue) { - window._httpQueue = []; - window._httpRun = 0; - } if (_httpQueue.length >= 5) return -1; // queue full; applyLED cancels stale queues before sending return new Promise(resolve => { _httpQueue.push({ cmd, resolve }); @@ -664,9 +661,7 @@ if (!palCache.length) return; const seq = ++_applySeq; // discard pending HTTP chunks from any previous preview so stale data doesn't drain slowly - if (window._httpQueue) { - while (_httpQueue.length) _httpQueue.shift().resolve(-1); // resolve dropped entries so their awaiters can observe the seq change and exit - } + while (_httpQueue.length) _httpQueue.shift().resolve(-1); // resolve dropped entries so their awaiters can observe the seq change and exit try { let st = await (await fetch(getURL('/json/state'), { cache: 'no-store' })).json(); if (seq !== _applySeq) return; // superseded by a newer preview request