Slider Changes Re-Creating WebGL Context?

Here’s an adaptation of your notebook that demonstrates how to mutate WebGL state:

In a sense, WebGL is a “worst-case scenario” for Observable because the API is inherently mutable: there’s a global WebGL context, and you need to mutate that context to render efficiently. Observable’s preference is for immutable state and pure functions, where you simply throw away the old state when referenced values change, and then create new start from the new referenced values.

But, since each cell in Observable is “just JavaScript”, you can totally use side-effects and mutation, as long as you understand the data flow graph of your notebook: how the cells depend on each other, and thus how changes in one cell will ripple through the other cells in your notebook.

Your notebook’s data flow graph looks like this:

Because the create cell depends on thickness, any change to thickness (such as making it a slider and interacting with it) causes the create cell to be re-run, throwing away your THREE.WebGLRenderer and other objects, and creating new ones from scratch.

In contrast, here’s the data flow graph in my notebook:

So here, thickness only feeds into render. The render cell then mutates the mesh and re-renders the scene, allowing it to render efficiently because it’s mutating the existing state rather than re-creating it from scratch.

The other way you preserve existing state rather than throwing it away is to use this, which stores the previous value of the current cell. Tom’s written a notebook on this values for more on that subject, and I’ll probably write more notebooks on the subject in the future, too.

(If you saw my earlier dependency graphs, note that I’ve inverted the orientation of edges in the graphs above in a way I hope is clearer: the arrows above represent the flow of values through the notebook, rather than the backwards references.)

5 Likes