It's probably browser settings or gpu per-application settings that limits it.
Btw preload shouldn't really be a problem. Calling it multiple times will just return a reference to the object. Resource itself is already loaded. No repeated loading is done. It's an inexpensive call.
My suspicion is that set_material() causes shader recompilation which is extremely expensive. I had a similar problem not so long ago. Even doing it sporadically may cause stutter.
So I'd also consider some of @cybereality's suggestions on having all marble versions prepared and just swapping them when downgrade needs to happen. Creating a new instance may be less expensive than the material change. Maybe try to run the profiler and see if material change causes any spikes in frame duration.