From 8ff9d4ed575dc2aa511ddf5ff7cb776a7f646eee Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Wed, 5 Aug 2026 21:14:46 -0700 Subject: [PATCH] perf: defer microphone permission during boot --- system/public/aesthetic.computer/bios.mjs | 40 ++++++++++++------- system/public/aesthetic.computer/lib/disk.mjs | 13 ++++++ tests/performance/boot-performance.test.mjs | 39 +++++++++++++++++- 3 files changed, 76 insertions(+), 16 deletions(-) diff --git a/system/public/aesthetic.computer/bios.mjs b/system/public/aesthetic.computer/bios.mjs index 7465356d4a..a42fdbeec7 100644 --- a/system/public/aesthetic.computer/bios.mjs +++ b/system/public/aesthetic.computer/bios.mjs @@ -4355,7 +4355,10 @@ async function boot(parsed, bpm = 60, resolution, debug) { }); } - const microphonePermission = await checkMicrophonePermission(); + // macOS/Chromium can take more than a second to answer this host permission + // query. It is useful state for microphone pieces, but it must not hold up + // the disk worker or the first rendered frame. + const microphonePermissionPromise = checkMicrophonePermission(); // Extract embedded source if available let embeddedSource = null; @@ -4384,7 +4387,7 @@ async function boot(parsed, bpm = 60, resolution, debug) { vscode: window.acVSCODE, objktMode: window.acPACK_MODE || false, objktKidlispCodes: window.objktKidlispCodes || globalThis.objktKidlispCodes || {}, - microphonePermission, + microphonePermission: null, resolution, embeddedSource, noauth: window.acNOAUTH || false, @@ -4404,9 +4407,20 @@ async function boot(parsed, bpm = 60, resolution, debug) { receivedChange(m); }; - let send = (msg) => { - console.warn("Send has not been wired yet!", msg); - }; + let send = (msg) => { + console.warn("Send has not been wired yet!", msg); + }; + + let firstMessageSent = false; + const sendFirstMessage = () => { + if (firstMessageSent) return; + firstMessageSent = true; + send(firstMessage); + consumeDiskSends(send); + microphonePermissionPromise.then((permission) => { + send({ type: "microphone-permission", content: permission }); + }); + }; const TAPE_PREVIEW_MAX_FRAMES = 90; const TAPE_PREVIEW_WIDTH = 256; @@ -4576,8 +4590,7 @@ async function boot(parsed, bpm = 60, resolution, debug) { module.noWorker.postMessage = (e) => onMessage(e); send = (e) => module.noWorker.onMessage(e); window.acSEND = send; - send(firstMessage); - consumeDiskSends(send); + sendFirstMessage(); }; if (worker.postMessage) { @@ -4618,8 +4631,7 @@ async function boot(parsed, bpm = 60, resolution, debug) { // NOW send the initial message - disk.mjs is ready to receive it perf.markBoot("first-message-sent"); - send(firstMessage); - consumeDiskSends(send); + sendFirstMessage(); return; } @@ -4656,8 +4668,7 @@ async function boot(parsed, bpm = 60, resolution, debug) { module.noWorker.postMessage = (e) => onMessage(e); send = (e) => module.noWorker.onMessage(e); window.acSEND = send; - send(firstMessage); - consumeDiskSends(send); + sendFirstMessage(); }, workerTimeoutMs); } else { // B. No Worker Mode @@ -4679,10 +4690,9 @@ async function boot(parsed, bpm = 60, resolution, debug) { // The initial message sends the path and host to load the disk. // Note: In worker mode, this is now handled inside the if(worker.postMessage) block // to ensure proper timing. In no-worker mode, it's handled here. - if (!workersEnabled) { - send(firstMessage); - consumeDiskSends(send); - } + if (!workersEnabled) { + sendFirstMessage(); + } // šŸ›‘ HALT Detection - Watchdog for unresponsive disk worker let lastPongTime = Date.now(); diff --git a/system/public/aesthetic.computer/lib/disk.mjs b/system/public/aesthetic.computer/lib/disk.mjs index 1bf6c72675..f2ed27815a 100644 --- a/system/public/aesthetic.computer/lib/disk.mjs +++ b/system/public/aesthetic.computer/lib/disk.mjs @@ -10642,6 +10642,19 @@ async function makeFrame({ data: { type, content } }) { return; } + if (type === "microphone-permission") { + microphone.permission = content; + if ( + content === "granted" && + globalKidLispInstance?.containsMicrophoneFunctions?.( + globalKidLispInstance.ast, + ) + ) { + globalKidLispInstance.requestMicrophoneConnection?.($activePaintApi); + } + return; + } + // Handle live reload from kidlisp.com editor if (type === "piece-reload") { log.piece.log("Reloading with new code:", content.source?.substring(0, 30) + "..."); diff --git a/tests/performance/boot-performance.test.mjs b/tests/performance/boot-performance.test.mjs index 3786bb4247..37d29b4c18 100644 --- a/tests/performance/boot-performance.test.mjs +++ b/tests/performance/boot-performance.test.mjs @@ -50,6 +50,33 @@ const BOOT_BUDGET = 6000; // ms, anon boot target — warn (don't fail) over thi async function installBootProbe(page) { await page.evaluateOnNewDocument(() => { window.__bootComplete = null; + window.__permissionQueries = []; + + // Permission lookups can cross into the host OS and quietly delay the + // critical path. Record their real duration before boot.mjs runs. + if (navigator.permissions?.query) { + const originalQuery = navigator.permissions.query.bind(navigator.permissions); + navigator.permissions.query = async (descriptor) => { + const started = performance.now(); + try { + const result = await originalQuery(descriptor); + window.__permissionQueries.push({ + name: descriptor?.name || "unknown", + ms: performance.now() - started, + state: result?.state || null, + }); + return result; + } catch (error) { + window.__permissionQueries.push({ + name: descriptor?.name || "unknown", + ms: performance.now() - started, + error: error?.message || String(error), + }); + throw error; + } + }; + } + const origFetch = window.fetch; window.fetch = function (url, opts) { try { @@ -147,6 +174,7 @@ async function run() { const paint = performance.getEntriesByType("paint"); return { timings: window._bootTimings || [], + permissionQueries: window.__permissionQueries || [], auth, ttfb: (nav.responseStart || 0) - (nav.requestStart || 0), fcp: paint.find((p) => p.name === "first-contentful-paint")?.startTime || 0, @@ -185,6 +213,15 @@ async function run() { console.log(""); } + if (data.permissionQueries.length) { + console.log("šŸ” permission queries:"); + for (const query of data.permissionQueries) { + const result = query.error ? `error: ${query.error}` : query.state; + console.log(` ${query.name}: ${query.ms.toFixed(0)}ms (${result})`); + } + console.log(""); + } + console.log("šŸ“¦ slowest resources:"); resources.forEach((r, i) => console.log(` ${i + 1}. ${r.name} — ${r.ms.toFixed(0)}ms ${r.size ? "(" + kb(r.size) + ")" : ""}`)); console.log(""); @@ -212,7 +249,7 @@ async function run() { mkdirSync(OUT_DIR, { recursive: true }); const stamp = new Date().toISOString().replace(/[:.]/g, "-"); const path = `${OUT_DIR}/boot-${stamp}.json`; - writeFileSync(path, JSON.stringify({ url: TEST_URL, bootMs, wallMs, ttfb: data.ttfb, fcp: data.fcp, complete: booted, timeline, auth: data.auth, resources, coverage: cov }, null, 2)); + writeFileSync(path, JSON.stringify({ url: TEST_URL, bootMs, wallMs, ttfb: data.ttfb, fcp: data.fcp, complete: booted, timeline, auth: data.auth, permissionQueries: data.permissionQueries, resources, coverage: cov }, null, 2)); console.log(`\nšŸ“„ baseline → ${path}`); } catch (e) { console.log(`\nāš ļø could not write baseline: ${e.message}`); -- 2.51.2