Working on the visualiser for an art piece (real-time view of light through a sculpted ac
rylic lens, plus an offline render path that exports to MP4) and there’s an intermittent glitch we can’t pin down. Hoping a fresh pair of eyes spots what we’re missing.
The symptom
During offline render (WebCodecs VideoEncoder into mp4-muxer), random individual frames come out with jagged pixelated patches across smooth glass surfaces. The artifact lands in different parts of the canvas each time, lasts exactly one frame, and is invisible during real-time playback. Two screenshots attached.
The pipeline
-
Three.js r170, single HTML file, no build step.
-
THREE.WebGLRenderer({ antialias: true, alpha: false, preserveDrawingBuffer: true }). -
EffectComposerwith postRT (HalfFloatType,samples: 4,SRGBColorSpace):RenderPass→UnrealBloomPass→ custom look-gradeShaderPass→OutputPass. -
Lens:
MeshPhysicalMaterialwithtransmission: 1, dispersion, IOR, thickness, roughness.transmissionResolutionScaledefaults to 1.0. -
Export loop:
composer.render()→ optional 4× SSAA →drawImage(renderer.domElement, ...)onto a 2DrecCanvasat output resolution →new VideoFrame(recCanvas, { timestamp })→encoder.encode(frame). Encoder backpressure handled with a smallawaitbetween frames.
What we’ve already tried
-
preserveDrawingBuffer: trueon the renderer (prime suspect for this symptom; reduced frequency but didn’t fully solve it). -
Guarded
window.resizeagainst firingrenderer.setSize()while a render is active (macOS menu bar, Dock, and DPI events were invalidating the transmission render target mid-render). -
Fixed a flag-name typo in the SSAA downscale path that was occasionally shipping the oversized canvas straight to the encoder.
-
Eight-frame warm-up before encoding starts so the transmission RT has time to fully initialise at high SSAA.
Frequency dropped after each fix, but the artifact still occasionally shows up. Current best guess is some interaction between the transmission render target and EffectComposer’s ping-pong RTs around the encoder read-back, but we’re out of theories.
Repo
Single-file source on GitHub: https://github.com/kitwebster-art/prismatica-app/blob/0f06865/index.html
renderOffline() starts at line 4378. Anyone who clones it can run a local server with the included serve.py and reproduce.
Any pointers gratefully received.

