diff --git a/public/preview.html b/public/preview.html
index fd7cbce..197753d 100644
--- a/public/preview.html
+++ b/public/preview.html
@@ -95,9 +95,17 @@
const $ = (s) => document.querySelector(s);
const TYPE_COLORS = { red: 0xff3b5c, yellow: 0xffc23b, blue: 0x3bb6ff };
- // Same VP9-alpha capability check as the hunt.
+ // VP9-alpha WebM transparency works on desktop Chromium/Gecko but NOT on
+ // WebKit: all iOS browsers (Safari, Chrome, Firefox) and desktop Safari play
+ // the WebM but render its alpha as opaque black. Those devices must fall
+ // through to the animated WebP (which IS transparent), so exclude WebKit here.
const SUPPORTS_WEBM_ALPHA = (() => {
try {
+ const ua = navigator.userAgent;
+ const isIOS = /iPad|iPhone|iPod/.test(ua) ||
+ (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); // iPadOS 13+ poses as Mac
+ const isSafari = /AppleWebKit/.test(ua) && !/Chrome|Chromium|Edg|OPR/.test(ua);
+ if (isIOS || isSafari) return false;
const v = document.createElement('video');
return !!v.canPlayType && v.canPlayType('video/webm; codecs="vp9"') !== '';
} catch { return false; }
@@ -215,11 +223,10 @@
vid.muted = true; vid.loop = true; vid.playsInline = true; vid.autoplay = true; vid.preload = 'auto';
vid.src = webm;
const tex = new THREE.VideoTexture(vid);
- // VP9-alpha WebMs carry straight (non-premultiplied) alpha. Leave the
- // texture in the default (linear/no-color-conversion) space — forcing
- // SRGBColorSpace here makes three.js crush the alpha to black. The
- // material below blends on the video's own alpha with premultipliedAlpha
- // off so transparent regions stay see-through.
+ // Desktop Chromium/Gecko only (WebKit excluded above). RGBAFormat keeps
+ // the alpha channel on frame upload; no SRGB override (it can crush the
+ // alpha); premultipliedAlpha off so transparent regions stay see-through.
+ tex.format = THREE.RGBAFormat;
tex.minFilter = THREE.LinearFilter; tex.magFilter = THREE.LinearFilter; tex.generateMipmaps = false;
const mat = new THREE.MeshBasicMaterial({
map: tex, transparent: true, side: THREE.DoubleSide,
@@ -272,7 +279,7 @@
const hasWebm = !!(data.webm_path || data.webm);
const hasImg = !!(data.image_path || data.image || data.webp_path || data.webp);
const kind = hasWebm
- ? ' · WebM' + (SUPPORTS_WEBM_ALPHA ? '' : ' (no VP9-alpha fallback)')
+ ? (SUPPORTS_WEBM_ALPHA ? ' · WebM' : ' · WebP (WebKit fallback)')
: hasImg ? ' · image' : ' · procedural wisp';
$('#pv-hint').textContent = `${data.name}${kind}`;
}