diff --git a/src/components/canvas/players/html5-player.ts b/src/components/canvas/players/html5-player.ts
index 37732243..7b6eb47d 100644
--- a/src/components/canvas/players/html5-player.ts
+++ b/src/components/canvas/players/html5-player.ts
@@ -376,6 +376,8 @@ export class Html5Player extends Player {
.join("");
const animationOverride = ``;
const bodyClone = doc.body.cloneNode(true) as HTMLElement;
+ // Scripts never run in an SVG image, and their source can hold characters XML forbids, which fail the frame.
+ bodyClone.querySelectorAll("script").forEach(script => script.remove());
const existingStyle = bodyClone.getAttribute("style") ?? "";
bodyClone.setAttribute("style", `width:${width}px;height:${height}px;margin:0;overflow:hidden;${existingStyle}`);
const bodyXml = serializer.serializeToString(bodyClone);
diff --git a/tests/html5-player.test.ts b/tests/html5-player.test.ts
index dfac1419..006db64c 100644
--- a/tests/html5-player.test.ts
+++ b/tests/html5-player.test.ts
@@ -124,6 +124,21 @@ it("captures inline SVG backgrounds and CSS metacharacters as valid XML without
expect(doc.head.querySelector("style")?.textContent).toBe(css);
});
+it("leaves scripts out of the capture so their source can't break the frame", () => {
+ const { harness, iframe } = createPlayer();
+ const doc = iframe.contentDocument!;
+ const script = doc.createElement("script");
+ script.textContent = 'const unit = "\u0001";';
+ doc.body.replaceChildren(script, Object.assign(doc.createElement("div"), { className: "title", textContent: "Shown" }));
+
+ const parsed = new DOMParser().parseFromString(harness.captureIframeAsForeignObjectSvg(1080, 1920), "image/svg+xml");
+
+ expect(parsed.querySelector("parsererror")).toBeNull();
+ expect(parsed.querySelector("script")).toBeNull();
+ expect(parsed.querySelector(".title")?.textContent).toBe("Shown");
+ expect(doc.body.querySelector("script")).not.toBeNull();
+});
+
it("keeps a failed capture inside its canvas layer and retries only after the asset changes", async () => {
const { player, harness, iframe, events, edit } = createPlayer();
jest.spyOn(console, "warn").mockImplementation(() => {});