diff --git a/public/preview.html b/public/preview.html
index 197753d..07a6747 100644
--- a/public/preview.html
+++ b/public/preview.html
@@ -246,13 +246,27 @@
const pr = vid.play(); if (pr && pr.catch) pr.catch(() => {});
group.userData.vidEl = vid;
} else if (image) {
+ // Animated WebP (the iOS/WebKit fallback). Three.js won't advance an
+ // animated-image Texture on iOS, so instead we draw the
into an
+ // offscreen canvas every frame and use a CanvasTexture. The browser keeps
+ // animating the WebP internally; drawImage() samples the current frame, so
+ // the texture animates and transparency is preserved.
const img = document.createElement('img');
img.crossOrigin = 'anonymous'; img.src = image;
- const tex = new THREE.Texture(img);
- img.onload = () => { tex.needsUpdate = true; };
+ const cnv = document.createElement('canvas');
+ cnv.width = 256; cnv.height = 256;
+ const ctx = cnv.getContext('2d');
+ const tex = new THREE.CanvasTexture(cnv);
+ tex.minFilter = THREE.LinearFilter; tex.magFilter = THREE.LinearFilter; tex.generateMipmaps = false;
+ img.onload = () => {
+ // size the canvas to the image's aspect once known
+ cnv.width = img.naturalWidth || 256;
+ cnv.height = img.naturalHeight || 256;
+ };
const mat = new THREE.MeshBasicMaterial({ map: tex, transparent: true, side: THREE.DoubleSide });
mesh = new THREE.Mesh(new THREE.PlaneGeometry(1, 1), mat);
- group.userData.gifTex = tex; group.userData.gifImg = img;
+ group.userData.canvasTex = tex; group.userData.canvasCtx = ctx; group.userData.canvasEl = cnv;
+ group.userData.animImg = img;
} else {
const geo = new THREE.SphereGeometry(0.4, 24, 24);
const mat = new THREE.MeshStandardMaterial({
@@ -311,7 +325,17 @@
if (current) {
current.position.y = Math.sin(t * 1.5 + current.userData.bobPhase) * 0.06;
current.lookAt(camera.position);
+ // Desktop WebM-onerror image fallback (rare): refresh its texture.
if (current.userData.gifTex && current.userData.gifImg?.complete) current.userData.gifTex.needsUpdate = true;
+ // Animated WebP path: redraw the current frame into the canvas texture
+ // so the animation plays (works on iOS, where image textures don't tick).
+ const ud = current.userData;
+ if (ud.canvasTex && ud.animImg?.complete && ud.animImg.naturalWidth) {
+ const c = ud.canvasEl;
+ ud.canvasCtx.clearRect(0, 0, c.width, c.height);
+ ud.canvasCtx.drawImage(ud.animImg, 0, 0, c.width, c.height);
+ ud.canvasTex.needsUpdate = true;
+ }
}
renderer.render(scene, camera);
}