From 4dd6d60457a46b708a0656b960388df5c0448cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Mon, 24 Aug 2026 13:46:26 +0200 Subject: [PATCH] Stop the pinball engine from hijacking the browser tab's favicon vpinball.js sets a table-supplied image as the page favicon via a blob: URL as a table loads. Revert any blob: href on the icon the moment it appears, via a MutationObserver, instead of patching the compiled engine. --- lib/pinball/usePinballInstance.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/pinball/usePinballInstance.ts b/lib/pinball/usePinballInstance.ts index 182f17e..f9c73d1 100644 --- a/lib/pinball/usePinballInstance.ts +++ b/lib/pinball/usePinballInstance.ts @@ -61,6 +61,32 @@ export function usePinballInstance( const canvas = canvasRef.current; if (!canvas || !webgl2Supported) return; + // The engine sets a table-supplied image as the page favicon via a + // blob: URL (see vpinball.js's rel~='icon' link handling) — undesirable + // here, where the app's own favicon should stay put. Snapshot the + // current icon links up front and revert any blob: href the moment it + // appears, rather than patching the (compiled) engine itself. + const originalIconHrefs = new Map(); + document.querySelectorAll("link[rel~='icon']").forEach((link) => { + originalIconHrefs.set(link, link.href); + }); + const faviconObserver = new MutationObserver(() => { + document.querySelectorAll("link[rel~='icon']").forEach((link) => { + if (!link.href.startsWith("blob:")) return; + const blobUrl = link.href; + const original = originalIconHrefs.get(link); + if (original) link.href = original; + else link.remove(); + URL.revokeObjectURL(blobUrl); + }); + }); + faviconObserver.observe(document.head, { + childList: true, + subtree: true, + attributes: true, + attributeFilter: ["href"], + }); + setStatus("loading"); const startedAt = performance.now(); @@ -103,6 +129,7 @@ export function usePinballInstance( })(); return () => { + faviconObserver.disconnect(); disposedRef.current = true; const instance = instanceRef.current; instanceRef.current = null;