The earlier "lil cube" example worked by creating individual box meshes and adding them to the scene. This is not a very scalable approach if we want a lot of boxes! Creating a single chunk of 16 x 16 x 32 blocks would take about 20ms to create: More than a single frame at 60 fps, and way too long to do anything dynamic.
Then next thing I tried was to merge the boxes into a parent geometry, rather than adding each mesh individually. This did indeed give a performance boost, with each chunk only taking 5 to 10ms to create. Good, but still pretty slow.
Finally, I went all in for building the geometry myself. Using Three.js's
BoxBufferedGeometry as a base, I wrote a function that would create the entire geometry for a single chunk. Each plane is added to a BufferGeometry - excluding any planes that would be obscured by a neigbouring box.
To work with Multi Materials (so you can have a different texture for each side of a cube) I had to split the creation into faces: rendering all the "top" faces first, then all the "front" faces and so on.
The result is a ~1ms render for most chunks on my machine: getting up higher with very complex and hole-y chunks.