melt
The transformation slice of *Pastel Astral Dream*: everything `film.melt(t)` drives. Hard, matte, dry and still becomes soft, glossy, wet, flowing and coming apart — and in this place what melts does not run down, it **lifts**.
melt
The transformation slice of Pastel Astral Dream: everything film.melt(t) drives.
Hard, matte, dry and still becomes soft, glossy, wet, flowing and coming apart — and
in this place what melts does not run down, it lifts.
Serves shots 6 (first drip), 7 (glacier), 9 (collapse), 10 (the column begins) and 12 (the sea drops).
What it puts in the world
Everything is spawned temporary = true under pad_melt_root and rebuilt from
build() on every scene load, so nothing here is written into the scene record.
| entities | what it is |
|---|---|
pad_melt_cap_1..26 | the mesa's upper 84 m, as 26 marshmallow pillows on a convex profile with four thin wafer strata through them. Sags, spreads and thins with the melt; from t≈62 its top leaves, scattering as it goes — this is the source of the rising column. |
pad_melt_lip | the melt lip on the mesa's +Z flank: a shelf whose underside sits at y = 107.2, which shot 6's beads hang from. Fixed on purpose — a macro station 1 m from a surface cannot follow a surface that sags 8 m. |
pad_melt_hero_1..5 | the five beads of shot 6, in the order they let go (37.3 → 44.7). The first is 14 cm across, sized for a 12 deg lens 1.3 m away. |
pad_melt_glacier_1..130 | the ice-cream glacier: 26 transverse slabs, each five bands — blueberry, pistachio, strawberry, vanilla and a wafer stratum. Upper bands advance further than lower ones, so the strata shear. |
pad_melt_calve_1..16 | blocks that leave the snout on their own clock (45.5 → 88.5) and drift up. The one at t=80.6 is shot 12's "last calved block". |
pad_melt_plinth, pad_melt_cone, pad_melt_scoop_1..9, pad_melt_glaze_1..2 | the 90 m scoop tower on its wafer plinth. |
pad_melt_puff_1..80 | the powdered sugar the collapse throws. |
pad_melt_shard_1..26 | candy-glaze shards that scatter across the plinth and stay there, so shot 12 can still see them. |
pad_melt_drip_1..216 | upward drips: 96 on the mesa's flank and under its lip, 44 on the glacier, 36 on the tower, 40 on the shallows. The shallows group rides the sea's surface down as it falls. |
pad_melt_riser_1..120 | ribbons and droplets leaving the mesa top — the first 300 m of the column, where it is still matter and not yet weather. |
Timings this slice fixes
| t | what happens |
|---|---|
| ~40 | the confection is wet enough that drips start everywhere |
| 37.3 – 44.7 | the five hero beads of shot 6, one letting go every ~1.2 s |
| 45.5 – 88.5 | sixteen calving events off the glacier snout |
| 61.2 | the scoop tower's top lets go, and the powdered-sugar burst |
| 61.2 / 61.9 / 62.6 | scoops 9, 8 and 7 release, in that order |
| 62 → 84 | film.column opens; the cap's top pillows lift and scatter |
What it reaches into, and how to turn that off
Two things this slice moves belong to the place slice, because the drop and the sag are melt's job while the geometry is place's:
- the sea — resolved by name from
pad_sea,pad_place_sea,pad_cream_sea,cream_sea,OceanSurface,Ocean, and moved tobaseY - film.seaDrop(t). - the mesa body — resolved from
pad_mesa,pad_place_mesa,pad_mesa_root,pad_marshmallow_mesa, and moved tobaseY - film.melt(t) * 18.
Both are re-resolved by NAME every frame and never held as a proxy, and both writes
are guarded: another slice rebuilding its content underneath this one used to take
the whole animator down with a stale-proxy error, and the only sign was one line in
the log. If place would rather own either, set M.driveSea = false /
M.driveMesa = false and this slice leaves it alone.
Add a name to SEA_NAMES / MESA_NAMES if place calls its entities something else.
What other slices may read
local melt = require("@user::pad.melt")
melt.wetness(t) -- 0..1, the gloss this slice puts on every confection surface
melt.capTopY(t) -- where the mesa's top surface actually is: it sags, then leaves
melt.stations -- the station each of shots 6, 7, 9, 10 and 12 was framed and checked at
melt.anchors -- capBaseY / capRadius / lipUnderside / dripHero / glacierSnout / burstAt
melt.stations.first_drip is the one that matters most: shot 6 is a 12 deg lens
1.3 m from a 14 cm bead, and every number in it — the eye, the look, the lens — was
found by looking at the frame. Moving the camera off that line loses the bead.
Two things worth knowing before editing this
Every animator here is a pure function of t. The tower's collapse is solved in
closed form (fallY — ballistic flight with two analytic bounces), every "random"
number comes out of frand(i, salt), a positional hash, and nothing accumulates.
A rigid body would integrate, and this film scrubs.
Wetness is not written to the .material assets. Material.setProperty and
Model:setMaterialProperty both land on the AUTHORED material — and that rewrites
its mat.yaml on disk, which at 24 fps is a file write per material per frame. So
build() mints a runtime twin of each authored material with
renderer.material.create(def, key) and points every entity of that colour at the
one twin with Model:applySessionMaterial(handle); the per-frame ramp then writes
the twin through renderer.material.setProperty, which touches no file. The
.material files under mat/melt/ stay the one source of truth for the FROZEN
look, and makeRuntimeMaterials() reads their values back off them rather than
duplicating the numbers here.
Scoped to this part · feeds back into the world's score.