The [voxel] table
A voxel preset runs a cellular automaton in three dimensions: a cube of cells, each generation
every cell deciding its next state from how many of its neighbours are live. The cube is drawn as a
glowing volume: a ray from the camera gathers the light of every cell it crosses, and each cell also
absorbs some of the light from behind it. [voxel] picks the rules, the cube and how it starts:
system = "voxel"
[voxel]grid = 64 # cells per side, 16..=128; the default is 64rules = ["clouds", "coral"] # 1..=8 rules, picked by the `rule` param; default ["clouds"]seed_radius = 1.0 # the seeded ball's radius, in half-sides, 0..=2seed_fill = 0.6 # the chance a cell in that ball starts live, 0..=1wrap = false # false (the default): every cell past a face is deadshells = 8 # radial shells the spectrum lights, 0..=16; default 0present = "glow" # "glow" (the default) or "solid"| Key | Values | Notes |
|---|---|---|
grid | integer 16..=128 | Cells per side. Like [cellular] grid it is content, not resolution: doubling it halves how large every structure looks. The tier caps it, and the app says so. |
rules | a list of 1 to 8 entries | Each entry is a roster name (below) or an inline rule. The rule parameter picks one by its place in the list, counting from 0. |
seed_radius | 0..=2 | The radius of the ball of cells the cube starts with, as a fraction of its half-side; 2 covers the whole cube. Absent, the first rule’s own. |
seed_fill | 0..=1 | The chance each cell inside that ball starts live. Absent, the first rule’s own. |
wrap | true / false | true makes the cube a torus, so a structure crossing a face comes back on the opposite one. Default false. |
shells | integer 0..=16 | How many radial shells the spectrum lights, bass at the centre and treble at the faces. 0 lights none, and shell_gain is then inert. |
present | "glow" or "solid" | How a live cell is drawn: as glowing light (the default), or as a lit solid cube (below). |
Every key is optional and so is the table: system = "voxel" alone runs clouds in a 64-cell cube.
Every key is read once, at load, and none is bindable. A grid, a seed or a shell count out of range,
an empty or over-long list, an unknown roster name, and an inline rule out of range are load
errors, and a rule’s error names its entry.
Every cell is drawn from the seed. The starting ball, and every ball a reseed refills, is
hashed from the preset’s [generator] seed, never from a clock, so the same preset driven by the
same music runs the same history.
A rule is two lists of counts
A cell is dead, live, or decaying. A dead cell with a count of live neighbours in birth is born;
a live cell with a count in survive stays live; a live cell that does not survive starts to
decay, steps one stage further each generation, and is dead again after its last stage. Only a
live cell counts as a neighbour. An inline rule writes this out:
[voxel]rules = [ "clouds", { birth = [4], survive = [4], states = 5, neighbourhood = "moore" },]| Key | Values | Notes |
|---|---|---|
birth | integers | Required. The live-neighbour counts at which a dead cell is born. |
survive | integers | Required. The counts at which a live cell stays live. |
states | integer 2..=255 | Dead, live and the decay stages. 2, the default, has no decay stages. |
neighbourhood | "moore" or "von_neumann" | moore (the default) counts the 26 cells sharing a face, an edge or a corner; a count runs 0..=26. von_neumann counts the 6 sharing a face; a count runs 0..=6. |
The 3D catalogues write a rule as survive/birth/states/neighbourhood, 4/4/5/M, which is the
reverse of the B…/S… order 2D Life uses. The roster below gives both lists by name, so nothing
needs translating.
The roster
Every roster rule is held by the test suite to a live fraction strictly between 0 and 0.9 of a
32-cell cube at generations 100 and 400, grown from its own seed. A list that opens with a roster
rule takes that rule’s seed when seed_radius and seed_fill are absent.
| Name | birth | survive | states | Neighbours | Seed (radius, fill) | What it does |
|---|---|---|---|---|---|---|
445 | 4 | 4 | 5 | Moore | 0.35, 0.2 | A sparse ball that burns out to a few still cells. |
amoeba | 5-7, 12-13, 15 | 9-26 | 5 | Moore | 2.0, 0.5 | A sparse field that swells into a mass filling most of the cube. |
builder | 4, 6, 8-9 | 2, 6, 9 | 10 | Moore | 0.5, 0.5 | A burst of long-decaying growth that leaves a still scatter of cells behind. |
clouds | 13-14, 17-19 | 13-26 | 2 | Moore | 1.0, 0.6 | A dense field that settles into rounded clouds. The default. |
coral | 6-7, 9, 12 | 5-8 | 4 | Moore | 0.5, 0.5 | A slowly churning reef with a skin of decaying cells. |
crystal | 1, 3 | 0-6 | 2 | von Neumann | 0.2, 0.3 | A small seed growing into a lattice that fills most of the cube. |
pyroclastic | 6-8 | 4-7 | 10 | Moore | 0.5, 0.3 | A restless, never-settling foam of cells and long decay. |
slow_decay | 13-26 | 1, 4, 8, 11, 13-26 | 5 | Moore | 1.0, 0.5 | A dense field that freezes almost at once. |
Most 3D rules settle. 445 and builder burn out to a still residue of a few cells, and
clouds, amoeba, crystal and slow_decay reach a shape and hold it. Only pyroclastic and
coral keep moving. reseed is how a cube stays alive, as on cellular: bind it to an onset or a
beat and every ball of fresh cells it drops grows again.
Switching rules on the bar
rule is a structural parameter: it is rounded, it picks an entry of rules by its place, and
past the end of the list it runs the last. A new rule takes effect at the next generation. Bound to
audio it would re-pick every frame, so hold it on a musical edge:
[voxel]rules = ["coral", "pyroclastic", "clouds"]
[params]rule = "mod(bar_index, 3)"
[hold]rule = "bar"A cell in a decay stage past the new rule’s states falls dead on the switch, so changing from a
rule with long decay to one with less can drop the volume’s haze at the bar line.
The music and the light
step_rateis generations per second, not per frame, so the automaton runs at the same speed at 30 fps and at 144.reseedfills one ball ofreseed_radiuswith fresh cells, once per rise past 0.5, at a centre drawn from the seed and the count of rises so far.shell_gainlights the cube by the spectrum: withshellsset, the band array is folded into that many concentric shells, bass at the centre, and each cell’s light is multiplied by1 + shell_gain × levelof its shell.densityis how strongly the volume absorbs the light behind it. At0it is pure additive glow, with no front or back: the far side shows through the near one. Higher gives the volume a surface and hides its inside.trailandage_tintpaint history. A decaying cell keepstrailof its light at each stage, sotrailis inert on a rule withstates = 2.age_tintslides a live cell along the palette as it stays live, and puts a decaying one at the far end.
Solid cells
present = "solid" draws every live cell as a cube with a lit surface instead of as light. A ray
from the camera stops at the first live cell it meets and takes the colour of the face it entered
through. The decay stages in front of that cell still glow and absorb as they do under glow, so a
solid structure can sit inside a haze of its own debris; trail and density set how much of it
there is.
[voxel]present = "solid"
[params]key = "0.4 + 1.6 * bass" # the key light's strengthambient = "0.3" # the light every face getsao = "1" # how far the creases darkenlight_yaw = "-0.6" # the light stands to the viewer's left...light_pitch = "0.6" # ...and aboveA face is shaded as its palette colour, times brightness, its shell’s light and the camera’s
fog, times ambient × occlusion + key × (how squarely the face turns toward the light).
keyis one directional light. A face turned straight at it takes all ofkey, one edge-on or turned away takes none.ambientis the light every face gets whichever way it turns. Withkey = 0andambient = 1every face reads its own palette colour.aois the ambient occlusion: a face darkens toward the creases and inner corners where it meets other cells, which is what makes a cluster of cubes read as a structure. It darkens the ambient term only.0is none.light_yawandlight_pitchplace the light relative to the camera, in radians:(0, 0)shines from the eye, a positivelight_yawmoves it to the viewer’s right and a positivelight_pitchabove. The light turns with an orbit, so the lit side stays lit. It is not fixed on the world.
These five are inert under glow, and the reference marks them so.
A lit face blooms like any other light. Nothing marks it as reflected light, so a face brighter
than the bloom threshold blooms, and a bright enough solid preset can glow until it reads emissive
again. Whether the faces bloom is a choice made through brightness and key: keep them under the
threshold for crisp cubes.
Trails smear shading as if it were light. The trails stage reads a dark face as dim light,
not as a surface, so on a turning camera it drags the shaded sides behind the lit ones. Leave
trails off a solid preset unless that smear is the look.
Cells that light each other
bounce lets a solid face take light from the live cells in front of it, so a structure glows
onto itself rather than only on screen. 0 is off and is the default, and the declared range runs
to 2.
[params]bounce = "1" # faces take their neighbours' lightbloom_amount = "0.3" # so the bloom can come downWhat it reads: the live cells, gathered into a coarse field of blocks four cells a side, each
holding how much of the block is live and how old its live cells are. The field is softened once,
and each lit face samples it half a cell in front of itself. A face adds its palette colour at the
neighbours’ mean age, times bounce, times how much light reached it, times brightness, its
shell’s light and the camera’s fog. Decaying cells give no light to it. The colour is looked up
when the face is drawn, so a bound hue or palette_mix moves the bounced light every frame. Only
how much light there is, and its age, wait for the next generation.
- It is solid-only. Under
glowevery cell already emits volumetrically, andbounceis inert there; the reference marks it so, like the five light parameters above. - It leaks through walls. The field has no notion of what blocks light, so a face on the far side of a thin slab is lit as readily as an open one. The light reaches about six cells. On thin structures this reads as translucency.
- It is soft by construction. One block of the field spans four cells, so the light cannot pick out a single neighbour. A crisp one-cell glow is out of its reach.
- The shell is the receiver’s. With
shell_gainon, the bounced light a face gets follows that face’s own band, not the bands of the cells that lit it. - It composes with bloom. Bounced light is light like any other: a face it lifts past the bloom
threshold blooms. With
bouncedoing the work inside the structure, a preset can lower itsbloom_amountand keep the glow off the background. - It ignores the section plane. The field is built from every live cell, cut or not, so with
cutbound a section face takes the light of the hidden cells behind it and glows. Following the plane would refill the field every frame a sweptcutmoves.
The field costs nothing until a frame binds bounce above 0. It is then built once and refilled
each time the automaton steps or a reseed lands, so its cost follows step_rate rather than the
frame rate.
Cutting the cube open
A dense rule drawn solid shows only its skin: the ray stops at the first live cell, so whatever the
structure holds inside is hidden. cut hides every cell beyond one flat plane, so the inside
shows as a section:
[params]cut = "0.5 + 0.12 * sin(time * 0.4)" # sweep the plane through the middlecut_yaw = "0.8" # which way the hidden side faces...cut_pitch = "0.4" # ...and how far it tilts up
present = "solid", rules = ["crystal"], cut sweeping about 0.5 — the teaching preset
docs/examples/voxel/section.toml
cutis how much of the cube the plane hides, measured along its facing:0hides nothing,0.5cuts through the centre,1hides everything. It is a parameter like any other, so it can sweep ontimeor open on the bass.cut_yawandcut_pitchturn the plane, in radians. At(0, 0)the hidden side is the cube’s+zhalf.cut_yawturns it around the cube’s vertical axis,cut_pitchtilts it toward the top (negative toward the bottom).
The plane turns with the structure, not with the camera. It is held in the cube’s own frame, so
an orbiting camera sees the same section from every side, and the cut side swings past the viewer
once a turn. A sweep bound to time and an orbit bound to time can line up into a section that
seems to stand still; give them different rates.
The section is stepped, at the size of a cell. A cell is cut whole or not at all, and a face
behind the plane is lit as every face is, so a tilted section reads as a staircase of cubes, fine
at grid = 64 and coarse at 16. Nothing caps the cut: a hollow structure shows its hollows. The
occlusion treats a cut cell as empty, so a section face is not darkened by cells the viewer cannot
see.
Only the view is cut. The automaton keeps running the hidden cells, and they still seed their
neighbours, so a structure keeps growing across the plane as if nothing were there. cut works
under glow too, where it opens a volume whose absorption hides its core.
The camera is the shared one: yaw, pitch, distance, fov and fog behave as on
plexus. focus and aperture are inert, because the volume has no drawn
primitive to blur, and the line systems’ solid parameter is not read here: a voxel is drawn
solid by present = "solid" above.
grid is capped by the quality tier: the Floor tier allows 64, the Rich tier 128. A preset
asking within 64 runs identically on both; one asking for more runs at the cap and says so.
Author against 64. The volume is also drawn at a resolution the tier caps, then stretched to the
window, which changes only how fine the picture is, never its shape.
The range that reads for each parameter is printed in the voxel table of
Parameter roster.
Built from f4f807a at version 0.178.0. This site tracks main and is not versioned per release.