Random single-frame pixel artifacts in WebCodecs export from EffectComposer + MeshPhysicalMaterial transmission

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 }).

  • EffectComposer with postRT (HalfFloatType, samples: 4, SRGBColorSpace): RenderPassUnrealBloomPass → custom look-grade ShaderPassOutputPass.

  • Lens: MeshPhysicalMaterial with transmission: 1, dispersion, IOR, thickness, roughness. transmissionResolutionScale defaults to 1.0.

  • Export loop: composer.render() → optional 4× SSAA → drawImage(renderer.domElement, ...) onto a 2D recCanvas at output resolution → new VideoFrame(recCanvas, { timestamp })encoder.encode(frame). Encoder backpressure handled with a small await between frames.

What we’ve already tried

  1. preserveDrawingBuffer: true on the renderer (prime suspect for this symptom; reduced frequency but didn’t fully solve it).

  2. Guarded window.resize against firing renderer.setSize() while a render is active (macOS menu bar, Dock, and DPI events were invalidating the transmission render target mid-render).

  3. Fixed a flag-name typo in the SSAA downscale path that was occasionally shipping the oversized canvas straight to the encoder.

  4. 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.

Any difference when using latest (.83) vs the .70 in your importmaps?
Have you confirmed this on different browsers/GPUs?

I cloned your repo and it feels quite laggy. Looks like a few things in the code are causing it, you can try:

  • Lower transmission resolution while moving the camera

  • Reduce dispersion, it adds a lot of instability

  • Make sure panel smoothing is enabled for both materials

  • Add adaptive quality based on camera movement

  • Maybe simplify postprocessing a bit during interaction

About the bug:

It looks like frames are being captured before the GPU finishes rendering. Since transmission and postprocessing use multiple render targets, sometimes a partially rendered frame gets picked up, which causes those random glitch frames. Adding a GPU sync before capture should fix it.