Experimenting with this 2D water shader... just one sprite, and you can easily draw the flow in the editor!
Still needs work, but I'll post a tutorial once I nail down the technique😅
#Unity
A glowing brush digs a cave into a hillside, then builds an arch out of thin air. Underneath: byte voxels and marching cubes, re-meshed one 16-voxel chunk at a time.
Watch it next to a height field 👇
▸ The problem
Most terrain is a height field: one height per column. It can raise a hill or dig a pit, but it cannot hold anything with space underneath. A cave or an arch needs at least two surfaces in the same column, and a height field only has one.
▸ How it works
The world is a 3D grid of bytes. Each one stores how solid that point is, and the surface sits where the value crosses 0.5. Terrain starts from layered noise: value = (y - height) x 0.5, clamped to 0..1.
Marching cubes walks every cell of eight voxels. Each corner below the threshold sets one bit of an 8-bit index. A 256-entry table says which of the 12 cell edges the surface crosses, and a vertex lands on each crossed edge by linear interpolation. A second table joins those vertices into up to five triangles.
The brush is the repo's deformer: every voxel within 3 units changes by 0.1 divided by its distance, every frame. Digging raises the values and building lowers them. Any chunk whose voxels changed is flagged and fully re-meshed.
▸ What I hit
Chunks share their border voxels: each one stores 17 per side for 16 cells. An edit near a chunk corner therefore flags up to 8 chunks. While digging, 1 or 2 chunks were rebuilt per frame. While building the arch, which crosses three chunk borders, it went up to 8.
Normals are one per triangle, so the surface comes out faceted. That is how the source does it.
▸ My build
Ported the voxel generation, the marching cubes loop with its lookup tables, the deformer and the chunk flagging to JavaScript, and drew the result with three.js.
65 x 33 x 65 byte voxels in 4 x 2 x 4 chunks of 16 cubed. The starting terrain has 14,218 triangles. After the cave and the arch it has 16,142.
Over the 24 s clip, chunks were rebuilt 1,308 times in total.
For the left half of the split I wrote a plain height field that moves each column's top to the edge of the brush sphere. That is about the closest a single height per column can get.
▸ Trade-offs
Bytes keep memory small, at the cost of only 256 density steps per voxel. Re-meshing a whole chunk for a one-voxel change wastes work. Flat normals look faceted. The triplanar shader here blends three flat colours by the surface normal. It does not use textures.
▸ When to use it
Diggable or buildable terrain, caves, overhangs and editor sculpting. For a big open landscape with no overhangs, a height field is lighter and still simpler.
Tell Claude to add these 8 things to instantly make hits feel good
1. Hit stop: freeze both for 5 frames on contact
2. Hit flash: the enemy turns pure white for 2 frames
3. Knockback: push it back 12 px, easing out over 8 frames
4. Screen shake: 3 px, settling over 10 frames
5. Particles: 10 drops splashing out of the cut
6. Impact spark: a small star where the blade lands, for 4 frames
7. Slash trail: an arc that follows the swing
8. Damage numbers: pop up, hop, blink out
On the killing blow, freeze for 9 frames instead of 5 and slow time down
None of these are rules. Pick the ones that fit your game and tweak them until it feels right