diff --git a/packages/web/src/Replay.svelte b/packages/web/src/Replay.svelte index 4b6ffbf..d0d72be 100644 --- a/packages/web/src/Replay.svelte +++ b/packages/web/src/Replay.svelte @@ -258,6 +258,10 @@ if (recorder) { recorder.stop(); return; } const cv = stageEl?.querySelector("canvas"); if (!cv) return; + // Recording means capturing the WHOLE reel: rewind to the top and + // roll. (Hitting record at the end used to auto-stop a beat later — + // a one-second clip of the final frame.) + idx = 0; const comp = document.createElement("canvas"); comp.width = 1280; comp.height = 720; @@ -287,17 +291,23 @@ compRaf = requestAnimationFrame(drawComp); }; compRaf = requestAnimationFrame(drawComp); - const mime = MediaRecorder.isTypeSupported("video/webm;codecs=vp9") - ? "video/webm;codecs=vp9" : "video/webm"; + // MP4 travels everywhere (iMessage, QuickTime, every platform); + // WebM is the fallback where the browser can't mux it. + const mime = [ + "video/mp4;codecs=avc1.42E01E", "video/mp4", + "video/webm;codecs=vp9", "video/webm", + ].find((m) => MediaRecorder.isTypeSupported(m)) ?? "video/webm"; + const container = mime.startsWith("video/mp4") ? "video/mp4" : "video/webm"; + const ext = container === "video/mp4" ? "mp4" : "webm"; const rec = new MediaRecorder(comp.captureStream(60), { mimeType: mime }); const chunks: Blob[] = []; rec.ondataavailable = (e) => { if (e.data.size) chunks.push(e.data); }; rec.onstop = () => { cancelAnimationFrame(compRaf); - const url = URL.createObjectURL(new Blob(chunks, { type: "video/webm" })); + const url = URL.createObjectURL(new Blob(chunks, { type: container })); const a = document.createElement("a"); a.href = url; - a.download = `wizwar-moves-${steps[0]?.seq ?? 0}-${steps[steps.length - 1]?.seq ?? 0}.webm`; + a.download = `wizwar-moves-${steps[0]?.seq ?? 0}-${steps[steps.length - 1]?.seq ?? 0}.${ext}`; a.click(); setTimeout(() => URL.revokeObjectURL(url), 2000); recorder = null;